侧边栏壁纸
  • 累计撰写 101 篇文章
  • 累计创建 89 个标签
  • 累计收到 9 条评论

spring系列笔记 - 第二十四章 Spring的高级注解

bearjun
2021-07-25 / 0 评论 / 1 点赞 / 1,272 阅读 / 7,756 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2021-07-25,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

配置Bean

Spring在3.x提供的新的注解,用于替换XML配置文件。

@Configuration
public class AppConfig{
  
}

@Configuration的本质是@Component注解的衍生注解。可查看源码。

@Bean注解

@Bean注解在配置bean中进行使用,等同于xml配置中的标签。

对象的创建

首先来会回顾下创建对象:

  • 简单对象:直接能够通过new的方式创建的对象
  • 复杂对象:不能直接通过new的方式创建的,比如Connection,SqlSessionFactory。

所有创建简单对象不就是直接注入:

@Configuration
public class AppConfig {
    
    @Bean
    public User user(){
        return new User();
    }
    等同于
    <bean id="user" class="com.bearjun.entity"/>
}
 
 
public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        User user = (User) ctx.getBean("user");
        System.out.println(user);
}

创建复杂对象还不是一样,需要什么,我们就返回什么

@Bean
public Connection conn(){
    Connection conn = null;
    try {
        Class.forName("com.mysql.jdbc.Driver");
        conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/users?useSSL=false","root","123456");
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return conn;
}

// 获取bean的时候:
Connection conn = (Connection) ctx.getBean("conn");

但是前面不是说过吗?一些遗留系统用的是FactoryBean的方式创建复杂对象,那又怎么操作呢?

// 通过FactoryBean来创建复杂对象
public class ConnectionFactoryBean implements FactoryBean<Connection> {
    public Connection getObject() throws Exception {
        Connection conn = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/users?useSSL=false","root","123456");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
 
    public Class<?> getObjectType() {
        return Connection.class;
    }
 
    public boolean isSingleton() {
        return false;
    }
}

//通过配置Bean整合这个FactoryBean创建的对象
@Bean
public Connection conn1() throws Exception {
    return new ConnectionFactoryBean().getObject();
}

如果想通过@Bean注解指定指定对象的id值,只需要在@Bean("id")即可。

控制对象的创建次数

用@Bean中,我们怎么像xml中控制对象创建次数呢?

<bean id="user" class="com.bearjun.entity" scope="singleton|prototype"/>

// @Scope相当于bean标签中的scope
@Bean("u")
@Scope("singleton|prototype")
public User user(){
    return new User();
}

当Spring创建对象的形式是默认形式(singleton),在工厂创建的时候,对象会同时被创建出来,如果你想在getBean的时候帮你创建这个单例对象,你只需要配置懒加载就可以,也就是lazy-init为true

当Spring创建对象的形式是prototype形式,一定只是在你getBean的时候工厂才帮你创建对象,而此时你无论配置什么懒加载,都不会生效。

其实原因很简单,你创建单例对象,整个工厂这个对象就一份,出于Spring的角度考虑,其初始化创建好了你直接拿来用,或者你想用的时候spring给你造一个。

注入
  • 用户自定义类型的注入

我们自己写的业务service或者dao。

// 基于配置文件的注入
<bean id="userService" class="com.chenxin.spring5.config.UserServiceImpl">
      <property name="userDao" ref="userDao"></property>
</bean>
 
<bean id="userDao" class="com.chenxin.spring5.config.UserDaoImpl"></bean>


// 基于@bean注解的注入
@Bean
public UserDao userDao() {
    return new UserDaoImpl();
}
 
@Bean
public UserService userService(){
    UserServiceImpl userService = new UserServiceImpl();
    // userDao()直接调用上述的方法,或者给本方法添加UserDao的参数。
    userService.setUserDao(userDao());
    return userService;
}
  • JDK类型的注入

对User的基本的成员变量进行赋值:

@Bean
public User user() {
    User user = new User();
    user.setName("bear");
    user.setPassword("123456");
    return user;
}

上述的userName和password都是写死的,也就是出现了耦合,怎么解决的呢?
定义一个配置文件application.properties,后写两个key,value,,结合上节的@PropertySource。

@Configuration
@PropertySource("application.properties")
public class AppConfig {
 
    @Value("${name}")
    private String name;
 
    @Value("${password}")
    private String password;
 
    @Bean
    public User user() {
        User user = new User();
        user.setName(name);
        user.setPassword(password);
        return user;
    }
}

@ComponentScan注解

作用:等同于XML配置文件中的<context:component-scan />标签
目的:进行相关注解的扫描 (@Component @Value @Autowired . . .)

基本使用
//加载配置Bean上:
@Configuration
// 扫描注解所在的包路径
@ComponentScan(basePackages = "com.bearjun.scan")
public class AppConfig2 {

}

<context:component-scan base-package=""/>
排除、包含的使用
  • 排除过滤
<context:component-scan base-package="com.bearjun">
        <context:exclude-filter type="assignable" expression="com.bearjun.entity.User"/>
    </context:component-scan>
 
 
@Configuration
@ComponentScan(basePackages = "com.chenxin.spring5",
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {User.class})})
public class AppConfig2 {
}
  • 包含过滤
<context:component-scan base-package="com.bearjun" use-default-filters="false">
 <context:include-filter type="" expression=""/>
</context:component-scan>
 
@ComponentScan(basePackages = "com.bearjun",
 useDefaultFilters = false,
 includeFilters = {@ComponentScan.Filter(type=
FilterType.ANNOTATION,value={Service.class})})

不管是那种策略,其中的值还是要注意下:

  • type = FilterType.ANNOTATION 对应 value
  • type = FilterType.ASSIGNABLE_TYPE 对应 value
  • type = FilterType.ASPECTJ 对应 pattern
  • type = FilterType.REGEX 对应 pattern
  • type = FilterType.CUSTOM 对应 value

Spring工厂创建对象的方式分析

多种配置方式的应用场景

62641-zatc6qahkr9.png

  • @Component注解,其各种衍生注解标注。【推荐】
  • @Bean注解,其他框架提供的类型。【推荐】
  • 传统的xml注入,遗留系统的整合。
  • @Import注入,springboot底层或者多配制bean的整合。
配置优先级

@Component及其衍生注解 < @Bean < 配置文件bean标签
说明:优先级高的配置会覆盖优先级低配置。

@Component及其衍生注解 < @Bean < 配置文件bean标签
优先级高的配置会覆盖优先级低配置 

@Component
public class User{

}

@Bean
public User user(){
  return new User();
}

<bean id="user" class="xxx.User"/>

配置覆盖条件:id值保持一致。

解决基于注解进行配置的耦合问题

应用场景:一个实现类,后期需要修改。在不修改代码的情况下。

<!-- applicationContext.xml 配置文件中进行覆盖 -->
<bean id="userDAO" class="com.bearjun.injection.UserDAOImplNew"/>

@Configuration
//@ImportResource("applicationContext.xml")
public class AppConfig4 {

    @Bean
    public UserDAO userDAO() {
        return new UserDAOImpl();
    }
}

// 重新创建一个配置文件,然后引入xml依赖,覆盖之前的配置。
@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig5{
  
}

整合多个配置信息

问题一:为什么会有多个配置信息?

  • 拆分多个配置bean的开发,是一种模块化开发的形式,也体现了面向对象各司其职的设计思想。

问题二:多配置信息整合的方式?

  • 多个配置Bean的整合。
  • 配置Bean与@Component相关注解的整合。
  • 配置Bean与SpringXML配置文件的整合。

问题三:整合多种配置需要关注那些要点?

  • 如何使多配置的信息 汇总成一个整体。
  • 如何实现跨配置的注入。
多个配置Bean的整合
  • base-package进行多个配置Bean的整合

45938-lelq3h6j79p.png
当然,在工厂创建时,指定多个配置Bean的Class对象。同时加载多个配置文件的class。

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig1.class,AppConfig2.class);
  • 通过 @Import(xxx.class)

把AppConfig1当作主配置Bean,在AppConfig1上添加@Import(Appconfig2.class),完成整合。
44810-xfl1h7fk4j.png

那么问题来了:如何跨配置进行注入?
直接通过@Autowired直接,把需要的对象作为本配置Bean的属性。

@Configuration
@Import(AppConfig2.class)
public class AppConfig1 {

    //把要跨配置注入的对象作为本配置Bean的属性
    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}

@Configuration
public class AppConfig2 {

    @Bean
    public UserDAO userDAO() {
        return new UserDAOImpl();
    }
}
配置Bean与@Component相关注解的整合

通过@ComponentScan(basePackages = " ")注解整合:

@Component(@Repository)
public class UserDAOImpl implements UserDAO{
  
}

@Configuration
@ComponentScan("")
public class AppConfig3 {
   
    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig3.class);
配置Bean与配置文件整合

使用 @ImportResource(“applicationContext.xml”) 注解整合

//1. 遗留系统的整合 2. 配置覆盖
  
public class UserDAOImpl implements UserDAO{
  
}
<bean id="userDAO" class="com.baizhiedu.injection.UserDAOImpl"/>

@Configuration
@ImportResource("applicationContext.xml")
public class AppConfig4 {
  
    @Autowired
    private UserDAO userDAO;

    @Bean
    public UserService userService() {
        UserServiceImpl userService = new UserServiceImpl();
        userService.setUserDAO(userDAO);
        return userService;
    }
}

ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig4.class);

配置Bean底层实现原理

Spring在配置Bean中加入了@Configuration注解后,底层就会通过Cglib的代理方式,来进行对象相关的配置、处理。
18065-bxqvfmre3no.png

四维一体的开发思想

问题一:什么是四维一体?

Spring开发一个功能的4种形式,虽然开发方式不同,但是最终效果是一样的。

  1. 基于schema
  2. 基于特定功能注解
  3. 基于原始<bean
  4. 基于@Bean注解

四维一体的开发案例:

  1. <context:property-placehoder
  2. @PropertySource 【推荐】
  3. @Bean 【推荐】
1

评论区