@Configuration用于定义配置类,替换掉xml配置文件
也就是说这个配置类就相当于配置文件
比如我创建一个Appconfig类指定为定义配置类就如下
@Configuration public class Appconfig { xxx }
创建对象的工厂也有所不同
之前都是ClassPathXmlApplicationContext等等
现在变为了:AnnotationConfigApplicationContex
使用方法有两种:
反射
ApplicationContext context =new AnnotationConfigApplicationContext(Appconfig.class);
指定所在的包(Appconfig在AnnotationProgramming下面)
ApplicationContext context= new AnnotationConfigApplicationContext("org/AnnotationProgramming");
@Bean注解在配置bean中进⾏使⽤,等同于XML配置⽂件中的
也有点类似@Component注解
直接new出返回即可
@Bean public User user(){ return new User(); }
在@Bean中直接完成
例如我要创建一个connetion对象
@Bean public Connection connection() throws ClassNotFoundException, SQLException { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_test","root","123456"); return connection; }
使用FactoryBean
声明一个ConnectionFactory类实现 FactoryBean接口
public class ConnectionFactory implements FactoryBean<Connection> { @Override public Connection getObject() throws Exception { Class.forName("com.mysql.cj.jdbc.Driver"); Connection connection= DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc_test","root","123456"); return connection; } @Override public Class<?> getObjectType() { return Connection.class; } @Override public boolean isSingleton() { return false; } }
在配置Bean中如下实现
简单来说步骤如下:
@Bean public Connection connectionFactoryConfig() { ConnectionFactory connectionFactory=new ConnectionFactory(); Connection connection= null; try { connection = connectionFactory.getObject(); } catch (Exception e) { e.printStackTrace(); } return connection; }
和之前的一样@Scope中的两个属性可以控制创建的时机
我们得把需要注入的类型给创建出来
比如我要注入UserDAO,那就得先创建出来
@Bean public UserDAO userDAO(){ UserDAO userDAO=new UserDAOImpl(); return userDAO; }
方式一:
直接声明为形式参数,前面必须先为其设置SET/GET方法
@Bean public UserService service(UserDAO userDAO){ UserServiceImpl userService=new UserServiceImpl(); userService.setUserDAO(userDAO); return userService; }
方式二:
直接调用方法注入
@Bean public UserService service1(){ UserServiceImpl userService=new UserServiceImpl(); userService.setUserDAO(userDAO());//userDAO()相当于返回的userDAO return userService; }
方式一:
直接手动设置,不过会有耦合问题
@Bean public Customer customer(){ Customer customer= new Customer(); customer.setId("1"); customer.setName("SY"); return customer; }
方式二:
通过配置文件设置,使用@PropertySource读取配置文件达到解耦
@PropertySource("classpath:/init.properties") public class Appconfig { @Value("${id}") private String id; @Value("${name}") private String name; @Bean public Customer customer(){ Customer customer= new Customer(); customer.setId(id); customer.setName(name); return customer; }
@ComponentScan注解在配置bean中进⾏使⽤,等同于XML配置⽂件中的context:component-scan标签
用于扫描相关注解
原理和之前一摸一样
1.如果要指定扫描的包则如下设置
@Configuration @ComponentScan(basePackages = "org.AnnotationProgramming.Scan") public class Appconfig_Scan { }
2.也可以在工厂创建时指定
Spring源码:
public AnnotationConfigApplicationContext(String... basePackages) { this(); scan(basePackages); refresh(); }
1.basePackages 指定包
2.excludeFilters 排除方案
3.@ComponentScan.Filter 指定排除类型 有下面4种
4.value和pattern
@Configuration @ComponentScan(basePackages = "org.AnnotationProgramming.Scan", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Repository.class}), @ComponentScan.Filter(type = FilterType.ASPECTJ,pattern = "*..san1")}) public class Appconfig_Scan { }
和上面一样只是excludeFilters 变为了includeFilters
@Configuration @ComponentScan(basePackages = "org.AnnotationProgramming.Scan", includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Repository.class})}) public class Appconfig_Scan { }
注意:若只写@ComponentScan则默认扫描所有配置类,在创建工厂时也会把所有的配置类的类给创建出来
到目前位置有多种配置形式均可进行创建对象,那么这些配置形式的应用场合有哪些?
在@Component,@Bean,配置文件存在优先级
@Component<@Bean<配置文件
优先级高的可以覆盖优先级低的
例如:
提供一个Customer类
它id和name应该是NULL
@Component public class Customer { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
接着在配置Bean中如下
@Configuration @ComponentScan(basePackages = "org.AnnotationProgramming.Service") @ImportResource("Applicontext_adv.xml")//后续会提到 public class Appconfig_adv { @Bean public Customer customer(){ Customer customer=new Customer(); customer.setName("SU"); customer.setId("1"); return customer; } }
按照之前说的应该会覆盖掉,那么创建工厂获取customer打印出运行结果为
2021-07-21 16:35:35 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'appconfig_adv' 2021-07-21 16:35:35 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'customer' Customer{id='1', name='SU'}
再接着再配置文件如下设置
<bean class="org.AnnotationProgramming.Service.Customer" id="customer"> <property name="id" value="2"/> <property name="name" value="SY"/> </bean>
按照之前说的应该会覆盖掉,那么创建工厂获取customer打印出运行结果为
2021-07-21 16:37:54 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'appconfig_adv' 2021-07-21 16:37:54 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'customer' Customer{id='2', name='SY'}
最重要的一点:每个配置形式的ID必须保持一致,比如我把@Component设置为id为CO
运行如下:
它会创建出@Bean对象而不是CO,也就不能进行覆盖
2021-07-21 16:39:59 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'CO' 2021-07-21 16:39:59 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'customer' Customer{id='2', name='SY'}
在上面配置Bean发现@ImportResource("Applicontext_adv.xml")有耦合,不符合开闭原则
也就是说,若以后要对配置Bean进行修改则要改动源代码
那么有以下解决方案
1.新建一个配置类,
Applicontext_adv.xml包含了需要覆盖的信息
如:
@Configuration @ImportResource("Applicontext_adv.xml") public class Appconfig_advNEW { }
那么在获取工厂时如下:
ApplicationContext context = new AnnotationConfigApplicationContext(Appconfig_adv.class,Appconfig_advNEW.class);
设置多个参数,就可以整合在一起了同时在配置文件中自由设置。
最终解耦还是得用配置文件完成,但是配置Bean开发也有很多好处,都是相辅相成同时开发
为什么会有多个配置信息?
拆分多个配置bean的开发,是⼀种模块化开发的形式,也体现了⾯向对象各司其职的设计思想
可以整合哪几种?
关注点
方式一:直接在AnnotationConfigApplicationContext中填入多个配置Bean
如:
Appconfig
@Configuration public class Appconfig { @Bean public User user1(){ User user=new User(); return user; } }
Appconfig1
@Configuration public class Appconfig1 { @Bean public User user2(){ User user=new User(); return user; } }
工厂使用
ApplicationContext context = new AnnotationConfigApplicationContext(Appconfig.class,Appconfig1.class);
方式二:直接在AnnotationConfigApplicationContext中填入配置Bean所在的包
如:
ApplicationContext context = new AnnotationConfigApplicationContext("org.AnnotationProgramming.Configs");
方式三:使用@Import
如:在Appconfig3上面使用@Import
@Configuration @Import(Appconfig1.class) public class Appconfig3 { @Bean public User user3(){ User user=new User(); return user; } }
那么在使用的时候会获取Appconfig1的内容
工厂中只需要这么使用
ApplicationContext context = new AnnotationConfigApplicationContext(Appconfig3.class);
一二测试代码:
//测试多个配置文件整合 @Test public void test() { ApplicationContext context = new AnnotationConfigApplicationContext("org.AnnotationProgramming.Configs"); User user1= (User) context.getBean("user1"); User user2= (User) context.getBean("user2"); System.out.println("user1 = " + user1); System.out.println("user2 = " + user2);
方式一二的结果都是一样的
都创建了user1和user2
2021-07-22 00:35:09 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'user1' 2021-07-22 00:35:09 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'user2'
方式三的测试代码
//测试@Improt @Test public void test11() { ApplicationContext context = new AnnotationConfigApplicationContext(Appconfig3.class); User user3= (User) context.getBean("user3"); User user2= (User) context.getBean("user2"); System.out.println("user3 = " + user3); System.out.println("user2 = " + user2);
可以看见创建了user2,user3
2021-07-22 00:36:45 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'user2' 2021-07-22 00:36:45 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'user3'
方式一:不跨配置的注入
直接使用@Autowired在需要的变量上面注解就好
如:
Appconfig4
@Configuration //不跨配置用 @Import(Appconfig5.class) //跨配置时用 //@ImportResource("Applicontext_adv.xml") public class Appconfig4 { @Autowired private UserDAO userDAO; @Bean public UserService userService(){ UserServiceImpl userService=new UserServiceImpl(); userService.setUserDAO(userDAO); return userService; } }
Appconfig5
@Configuration public class Appconfig5 { @Bean UserDAO userDAO(){ UserDAO userDAO=new UserDAOImpl(); return userDAO; } }
方式二:跨配置的注入
注释掉@Import 使用@ImportResource指定配置文件中创建的UserDAO对象
<bean id="userDAO" class="org.AnnotationProgramming.DAO.UserDAOImpl"/>
测试代码:
//测试注入 @Test public void test12() { ApplicationContext context = new AnnotationConfigApplicationContext(Appconfig4.class); UserServiceImpl service = (UserServiceImpl) context.getBean("userService"); service.register(); }
运行结果:
都是一样的,可以看到UserDAOImpl.save被打印说明被执行,也就是注入成功
2021-07-22 00:45:28 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'appconfig4' 2021-07-22 00:45:28 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'userDAO' 2021-07-22 00:45:28 DEBUG DefaultListableBeanFactory:225 - Creating shared instance of singleton bean 'userService' UserDAOImpl.save
如下:在之前有过,所以不过多介绍
@Configuration //功能注解用 //@PropertySource("classpath:/init.properties") //配置文件用context:property-placeholder //@ImportResource("Applicontext_adv.xml") public class Appconfig1 { @Value("${id}") private String id; @Value("${name}") private String name; @Bean public Customer customer(){ Customer customer= new Customer(); customer.setId(id); customer.setName(name); return customer; }
这是在Spring配置文件中使用
如下设置;
<context:property-placeholder location="classpath:init.properties"/>
代码层面把上面的注释取消即可
当然了使用ClassPathXmlApplicationContext工厂也是可以的,不过需要先创建好Customer
配置互通
注意:
记得加上DTD约束
xmlns:context="http://www.springframework.org/schema/context" #在xsi:schemaLocation加入 http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
如下:其他和第二条一样
<bean id="holder" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="location" value="init.properties"/>
如下:
需要指出的是必须是整合其他配置类,要不然读取不到配置文件内容
可能和配置类的CGlib代理有关,暂时不研究
@Configuration @Import(Appconfig1.class) public class holder { @Bean public PropertySourcesPlaceholderConfigurer configurer(){ PropertySourcesPlaceholderConfigurer configurer=new PropertySourcesPlaceholderConfigurer(); configurer.setLocation(new ClassPathResource("init.properties")); return configurer; } }
测试代码:
@Test //测试4种proerties的注入 public void test13() { ApplicationContext context = new AnnotationConfigApplicationContext(Appconfig1.class); // ApplicationContext context=new ClassPathXmlApplicationContext("/Applicontext_adv.xml"); Customer customer= (Customer) context.getBean("customer"); System.out.println(customer.toString()); }
结果都是
Customer{id='123', name='sy'}