Spring6注解式开发如何实现高效策略?

摘要:spring框架创建bean就是利用反射机制 反射机制的代码如下: public static void main(String[] args) throws Exception { System.out.println(&
spring框架创建bean就是利用反射机制 反射机制的代码如下: public static void main(String[] args) throws Exception { System.out.println("Hello, World!"); // 使用反射机制调用方法 // 获取类 Class<?> clazz = Class.forName("com.ali.bean2.SomeService"); // 获取方法 Method method = clazz.getMethod("doSomething", String.class, int.class); // 获取对象 Object obj = clazz.newInstance(); // 方法调用 // obj: 哪个对象调用这个方法 // "hello", 42: 方法参数 // hello: 方法的返回值 Object hello = method.invoke(obj, "hello", 42); } spring IoC注解式开发 注解主要是为了简化xml配置。 注解怎么定义 新建Java Class 时选择Annoation 类型的文件,这样就创建了一个新的注解 // 自定义注解 // @Target 标注注解的注解,叫做元注解 // ElementType.TYPE: 表示可以标注在类上 // ElementType.FIELD: 表示可以标注在属性上 // 使用某个注解的时候,如果注解的属性名是value,可以省略属性名 // 使用某个注解的时候,如果注解的属性值是数组,并且数组中只有一个值,可以省略大括号 @Target(value = {ElementType.TYPE,ElementType.FIELD}) // @Retention 标注注解的生命周期,叫做元注解,表示最终保留在class文件中,并且可以被反射机制读取 // RetentionPolicy.SOURCE: 注解只在源码中存在,编译成字节码后就不存在了 // RetentionPolicy.CLASS: 注解在源码和字节码中都存在,运行时不存在(默认值),也就是不能被反射机制读取 // RetentionPolicy.RUNTIME: 注解在源码、字节码和运行时都存在 @Retention(RetentionPolicy.RUNTIME) public @interface Component { String value(); } 怎么通过反射机制读取注解 假设user类被注解@Component修饰 @Component("userBean") public class User { } public static void main(String[] args) throws Exception { // 使用反射机制读取注解 // 获取类 Class<?> userClazz = Class.forName("com.ali.component.User"); // 判断类上是否有某个注解 boolean hasAnnotation = userClazz.isAnnotationPresent(Component.class); if (hasAnnotation) { // 获取类上的注解对象 Component component = userClazz.getAnnotation(Component.class); // 访问注解的属性 System.out.println("component value: " + component.value()); } } 组件扫描原理 主要是通过反射机制实例化注解标记的类的对象。 public static void main(String[] args) { // 根据一个包名,扫描这个包下面的所有类,当这个类有@Component注解时,实例化这个类,key是@Component注解的value,value是实例化的对象 String packageName = "com.ali.component"; // 将包名中的“.”替换成“/” String path = packageName.replaceAll("\\.", "/"); // 包是在系统恨路径下的一个目录。获取它的绝对路径 String absolutePath = ClassLoader.getSystemClassLoader().getResource(path).getPath(); // 获取包下面的所有类文件 File dir = new File(absolutePath); File[] files = dir.listFiles(); Arrays.stream(files).forEach(file -> { // 获取类名 String classname = packageName + "." + file.getName().replace(".class", ""); // 通过反射机制解析注解 try { Class<?> clazz = Class.forName(classname); // 判断类上是否有某个注解 boolean hasAnnotation = clazz.isAnnotationPresent(Component.class); if (hasAnnotation) { // 获取类上的注解对象 Component component = clazz.getAnnotation(Component.class); // 访问注解的属性 System.out.println("component value: " + component.value()); // 实例化这个类 Object obj = clazz.getDeclaredConstructor().newInstance(); System.out.println("实例化对象: " + obj); } } catch (Exception e) { e.printStackTrace(); } }); } 声明bean的注解 声明bean的注解有:@Component 、@Controller、 @Service 、@Repository。 实际上 @Controller @Service @Repository 这3个都是@Component 的别名,本质上就是一个注解。主要是为了增强代码的可读性。 当这4个注解标注在类上是,如果没有指定value值(也就是没有指定bean名称),那么bean名称默认成类名的首字母小写。 选择性实例化bean 假如有一个需求:只需要@Controller参与bean的管理。其他三个注解都不参与实例化,这种情况要怎么处理? 注意:别忘了添加配置文件的命名空间。 <!-- 第一种方案:使用@ComponentScan自动扫描bean use-default-filters="false" 表示不使用默认的过滤器(即所有声明bean的注解全部失效) @Component 、@Controller、 @Service 、@Repository 全部失效--> <context:component-scan base-package="com.ali.bean2" use-default-filters="false"> <!-- 自定义过滤器,指定只扫描带有@Component注解的类 只有@Component生效--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Component"/> <!-- 如果想让@Service生效,可以添加下面这一行--> <context:include-filter type="annotation" expression="org.springframework.stereotype.Service"/> </context:component-scan> <!-- 第二种方案:use-default-filters="true" 或者不写该属性,表示 @Component 、@Controller、 @Service 、@Repository 全部生效--> <context:component-scan base-package="com.ali.bean2" > <!-- 排除@Controller注解的类 只有@Controller生效--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> <!-- 如果想让@Repository失效,可以添加下面这一行--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Repository"/> </context:component-scan> 负责注入的注解 @value @value:使用@value注入的话,属性可以不提供setter方法,负责注入简单类型。 public class User { @Value("Alice") private String name; @Value("22") private int age; } @value注解也可以加在setter方法上 public class User { private String name; private int age; @Value("Alice") public void setName(String name) { this.name = name; } @Value("22") public void setAge(int age) { this.age = age; } } @value注解也可以加在构造方法上 public class User { private String name; private int age; public User(@Value("Alice") String name,@Value("22") int age) { this.name = name; this.age = age; } } @Autowired @Autowired可以用来注入非简单类型,翻译为:自动装配。 单独使用 @Autowired注解,默认根据类型装配【默认是byType】 注意:假如一个接口被2个或以上的类实现,那么这个接口对象能被 @Autowired注入吗?这当然不行,因为 @Autowired是根据类型进行装配的。 那怎么解决这个问题呢? 可以将@Autowired和@Qualifier联合使用 @Autowired // 这里指定bean的名称,说明是根据名称进行自动装配的 @Qualifier("someServiceBeanForMysql") private SomeService someService; @Autowired可以标注在属性上、setter方法上、构造方法的参数上。 当一个类中的构造方法只有一个,并且构造方法的参数和属性能对应上,@Autowired 可以省略 (不建议这样用) @Resource @Resource注解也能完成非简单类型的注入,那么他和 @Autowired有什么区别呢? @Resource是jdk扩展包中的,属于jdk的一部分,所以该注解是标准注解,更加具有通用性。 @Autowired是spring框架自己的 @Resource默认根据名称自动装配,未指定name时,使用属性名作为name,如果通过name找不到的话。会启动通过类型自动装配。 @Autowired时根据类型自动装配。如果要根据名字装配,需要@Qualifier配合使用 @Resource用在属性上、setter方法上 @Autowired用在属性上、setter方法上、构造方法上、构造方法参数上。 使用@Resource时先加入相关依赖 <!-- @Resource注解的依赖--> <dependency> <groupId>jakarta.annotation</groupId> <artifactId>jakarta.annotation-api</artifactId> <version>2.1.1</version> </dependency> // 进行属性注入 @Resource(name = "someServiceBean") private SomeService someService;