• Spring基于纯注解方式的使用


    经过上篇xml与注解混合方式,对注解有了简单额了解,上篇的配置方式极大地简化了xml中配置,但仍有部分配置在xml中进行,接下来我们就通过注解的方式将xml中的配置用注解的方式实现,并最终去掉xml配置。

    一、xml中遗留配置

    注解扫描

    <!-- 开启注解并扫描指定包中带有注解的类 -->
    <context:component-scan base-package="com.kkb.spring.service"/>
    

    非自定义bean,如sqlsessionFactory

    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" value="dataSource"></property>
    </bean>
    

    下面将用注解的方式进行替代。

    二、组件注册

    首先是@Configuration注解,被其修饰的类可看做xml配置文件,通过此类进行组件注册。而上下文容器可通过下面方式进行创建:

    ApplicationContext context = new    AnnotationConfigApplicationContext(SpringConfiguration.class);
    

    其中,SpringConfiguration是被@Configuration修饰的配置类。

    1、@Bean注解

    @Bean是Spring提供的注解,它在配置类中使用,可以将Java类交给Spring进行管理,@Bean有如下特征:

    • 默认时,以@Bean修饰的bean对象对应的关键字是【方法名】
    • 如果在@Bean指定bean对象对应关键@Bean(value={"stu1","stu2"}),此时bean对象在Spring容器对应关键字就不是【方法名】,是stu1或则stu2
    • 所有通过@Bean修饰生成的bean对象默认的情况下都是单例。
    • 对于单例的bean对象,可以通过@Lazy延缓变对象被创建时机。
    @Configuration//相当于配置文件
    public class ApplicationConfig {
        
        @Bean(value={"stu1"})
    	public Student student2(){
    		return new Student();
    	}
        
        //这个注解专用于单例模式bean对象,此时bean对象不会在,spring容器启动时被创建的,只有在一个用户来访时才会被创建
        @Lazy
        @Bean
        public Teacher teacher(){
        	return new Teacher();
        }
    }
    

    2、@ComponentScan注解

    @ComponentScan是Spring中的注解,在配置类中使用,用于配置扫描的类,它可以将某些类排除在Spring容器之外,也可以将某些类添加到Spring容器之内 。相当于context:component-scan标签

    @ComponentScan(value="com.kkb.beans")
    @Configuration//相当于配置文件
    public class ApplicationConfig {}
    

    可以在@ComponentScan中配置FilterType进行扫描文件的过滤,FilterType提供五种扫描策略:

    • ANNOTATION 根据注解进行过滤(@Controller,@Service,@Resposity,@Compoent)
    • ASSIGNABLE_TYPE 根据指定类型
    • ASPECTJ表达式过滤
    • REGEX根据正则表达式过滤
    • CUSTOM,根据开发人员自行定义过滤规则

    2.1、将指定类添加到Spring容器中

    将指定的类添加Spring容器使用includeFilters

    @ComponentScan(value="com.luis.beans",useDefaultFilters=false,
                 includeFilters={
                		  @Filter(type=FilterType.ANNOTATION,
                				   value={Controller.class,Service.class}),
                		   
                		  @Filter(type=FilterType.ASSIGNABLE_TYPE,
                		          value={DeptDao.class})  	   
                   })
    @Configuration//相当于配置文件
    public class ApplicationConfig {
    
    }
    

    上面的代码采用了1,2两个策略将指定类添加到Spring容器中。

    自定义扫描规则的配置如下:

    在@ComponentScan(value="包路径",
                  excludeFilters={
                            @Filter(type=FilterType.CUSTOM,value=自定义过滤规则类.class)
                  })
    @Configuration//相当于配置文件
    public class ApplicationConfig {
    
    }
    

    此外还需配置过滤规则类。

    2.2、将指定类排除Spring容器外

    将指定类排除Spring容器外使用excludeFilters

    @ComponentScan(value="com.luis.beans",
                   excludeFilters={
                		   @Filter(type=FilterType.ANNOTATION,
                				   value={Controller.class,Service.class})  
                   })
    @Configuration//相当于配置文件
    public class ApplicationConfig {
    
    }
    

    3、@Conditionnal注解

    @Conditionnal是Spring提供的注解,用在配置类中,用于动态决定是否添加进Spring容器中。

    现有两个类:Student和Teacher

    //如果当前工程运行在Windows系统下,就注册Student
    public class Student {}
    
    //如果当前工程运行在Linux系统下,就注册Teacher
    public class Teacher {}
    

    定义两个判断类:LinuxCondition和WindowsCondition

    public class LinuxCondition implements Condition {
    	/*
    	 * ConditionContext context:spring容器上下文环境
    	 * AnnotatedTypeMetadata metadata :@Conditional修饰类型信息
    	 */
    	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    		  String systemName = context.getEnvironment().getProperty("os.name");
    		  if(systemName.contains("Linux")){
    			  return true;
    		  }
    		  return false;
    	}
    }
    
    public class WindowsCondition implements Condition {
    	
    	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
    		   String systemName = context.getEnvironment().getProperty("os.name");
    		   if(systemName.contains("Windows")){
    			   return true;
    		   }
    		return false;
    	}
    }
    

    配置类代码:

    @Configuration//相当于配置文件
    public class ApplicationConfig {
    
    	@Conditional({LinuxCondition.class})
    	@Bean
    	public Teacher teacher(){
    		return new Teacher();
    	}
    	
    	@Conditional({WindowsCondition.class})
    	@Bean
    	public Student student(){
    		return new Student();
    	}
    }
    

    4、@Import注解

    用来组合多个配置类, 相当于spring配置文件中的import标签,在引入其他配置类时,可以不用再写@Configuration 注解。也可以指定将指定bean导到SpringIOC容器中

    @import(value="com.luis.Student")
    @Configuration//相当于配置文件
    public class ApplicationConfig {
    
    }
    

    此外,可通过自定义的选择器将bean添加到IOC容器中。

    public class MyImportSelector implements ImportSelector {
    
    	public String[] selectImports(AnnotationMetadata importingClassMetadata) {
    		String classNames[]={"com.luis.Student"};
    		return classNames;
    	}
    }
    
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    
    	public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    		//1.将Java类注册到Spring
    		BeanDefinitionBuilder builer  = BeanDefinitionBuilder.genericBeanDefinition(Teacher.class);
    		//2.创建当前Java类的实例对象
    		BeanDefinition obj= builer.getBeanDefinition();
    		
    		//3.通过Spring的bean注册器,将当前Java类的实例对象添加到Spring容器
    		registry.registerBeanDefinition("luis", obj);
    	}
    }
    

    配置类代码如下:

    @Import(value={MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
    @Configuration//相当于配置文件
    public class ApplicationConfig {
        //两种添加方式
    }
    

    5、FactoryBean接口

    FactoryBean是Spring容器提供的一种注册bean的方式,通过它来获得组件的bean的对象以及bean对象的单例或则多例的形态。

    public class MyFactoryBean implements FactoryBean<Student> {
    
    	//通知Spring容器,当前Student类的实例对象创建方式
    	public Student getObject() throws Exception {
    		return new Student();
    	}
    
    	//通知Spring容器,被管理的bean对象在spring容易对应的类型
    	public Class<?> getObjectType() {
    		return Student.class;
    	}
    
    	/*
    	 *   true 单例
    	 *   false prototype
    	 * */
    	public boolean isSingleton() {
    		return true;
    	}
    }
    

    配置类代码如下:

    @Configuration//相当于配置文件
    public class ApplicationConfig {
        @Bean
    	 public FactoryBean factoryBean(){
    		 return new MyFactoryBean();
    	 }
    }
    

    6、@PropertySource注解

    写在配置类中,用于 加载properties配置文件,相当于context:property-placeholder标签

    properties文件:

    jdbc.driver=com.mysql.jdbc.Driver 
    jdbc.url=jdbc:mysql:///spring
    jdbc.username=root 
    jdbc.password=root
    

    配置类:

    @Configuration
    @PropertySource("classpath:jdbc.properties")
    public class JdbcConfig {
    	@Value("${jdbc.driver}")
    	private String driver;
    	@Value("${jdbc.url}")
    	private String url;
    	@Value("${jdbc.username}")
    	private String username;
    	@Value("${jdbc.password}")
    	private String password;
    
    	/**
    	 * 创建一个数据源,并存入 spring 容器中
    	 */
    	@Bean(name = "dataSource")
    	public DataSource createDataSource() {
    		try {
    			ComboPooledDataSource ds = new ComboPooledDataSource();
    			ds.setDriverClass(driver);
    			ds.setJdbcUrl(url);
    			ds.setUser(username);
    			ds.setPassword(password);
    			return ds;
    		} catch (Exception e) {
    			throw new RuntimeException(e);
    		}
    	}
    }
    

    二、bean对象赋值

    1、@Value注解

    @Value可在当前类中指定属性上赋值,其有以下几种用法:

    public class Student {
    
    	@Value("luis")//使用基本数据为属性赋值
    	private String sname;
    	@Value("#{28-2}")//使用SPEL为属性赋值
    	private int age;
    	@Value("${student.home}")//读取来自于外部的properties属性文件内容
    	private String home;
    }
    

    2、BeanPostProcessor

    为所有的类的指定属性赋值BeanPostProcessor(后置处理器),BeanPostProcessor接口的自定义直接实现类可以在当前Spring容器的 所有bean对象初始化前后被调用。

    BeanPostProcessor是一个接口,主要用于在bean对象初始化前后,做一些辅助功能

    其中,postProcessBeforeInitialization:bean被初始化之前工作,postProcessAfterInitialization:被初始化之后工作

    @Component
    public class MyBeanPostProcessor implements BeanPostProcessor {
    
    	//在bean对象初始化之前被调用 bean是Spring容器管理一个对象,beanName就是当前对象在Spring容器关联关键字
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		
    		if(bean.getClass()==Dog.class){
    		  try{
    			  Field field =	bean.getClass().getDeclaredField("age");
    			  field.setAccessible(true);
    			  field.set(bean, 15);
    		  }catch(Exception ex){
    			  ex.printStackTrace();
    		  }
    		}else if(bean.getClass()==Bird.class){
    			 try{
    				  Field field =	bean.getClass().getDeclaredField("age");
    				  field.setAccessible(true);
    				  field.set(bean, 9);
    			  }catch(Exception ex){
    				  ex.printStackTrace();
    			  }
    		}
    		return bean;
    	}
    
    	//在bean对象初始化之后被调用 bean是Spring容器管理一个对象,beanName就是当前对象在Spring容器关联关键字
    	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    		return bean;
    	}	
    }
    

    配置类:

    @ComponentScan(value={"com.luis.beans"})
    @Configuration//相当于配置文件
    public class ApplicationConfig {}
    

    BeanPostProcessor的原理是:遍历Spring容器中所有的BeanPostProcessor,执行每一个BeanPostProcessor中的 postProcessBeforeInitialization方法,如果某一次执行返回null.则立刻结束执行。

    三、Bean的生命周期

    1、@Bean注解处理Bean

    可以在类文件中定义初始化或销毁方法,通过@Bean注解进行方法的指定。

    public class Student{
        public void init() {
    		System.out.println("对象被创建");
    	}
    
    	public void destory() {
    		System.out.println("对象被回收");
    	}
    }
    
    @Configuration//相当于配置文件
    public class ApplicationConfig {
         @Bean(initMethod="init",destoryMethod="destory")
        public Student student(){
        	return new Student();
        }
    }
    

    2、InitializingBean&DisposableBean

    我们可以让类文件同时实现InitializingBean接口与DisposableBean接口,根据这两个接口提供的监听方法来监听当前类的bean的实例化时机和销毁时机。

    public class Student implements InitializingBean, DisposableBean {
    
    	public Student() {
    		System.out.println("Student构造方法被调用");
    	}
    
    	public void destroy() throws Exception {
    		System.out.println("Student对象被销毁");
    	}
    
    	public void afterPropertiesSet() throws Exception {
    		System.out.println("Student对象被初始化");
    	}
    }
    

    3、@PreDestroy& @PostConstruct

    可以在类中指定方法上添加这两个注解,来指定监听bean对象被实例化和销毁的时机。这两个注解不是由Spring提供的。

    public class Student{
    
    	public Student() {
    		System.out.println("Student构造方法被调用");
    	}
    	
    	@PreDestroy
    	public void destroy() throws Exception {
    		System.out.println("Student对象被销毁");
    	}
    
    	@PostConstruct
    	public void afterPropertiesSet() throws Exception {
    		System.out.println("Student对象被初始化");
    	}
    }
    

    四、Junit的使用

    在进行单元测试的过程中,每个方法都需要创建上下文容器,他们是不可或缺的,但又与我们的只写业务代码的理念相违背,不过好在Junit 给我们暴露了一个注解(@RunWith),可以让我们替换掉它的运行器。这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器,使用时只要指出配置文件的位置。其步骤如下:

    • 添加依赖包:spring-test
    • 通过@RunWith注解,指定spring的运行器,Spring的运行器为:SpringJunit4ClassRunner
    • 过@ContextConfiguration注解,指定spring运行器需要的配置文件路径
    • 通过@Autowired注解给测试类中的变量注入数据

    示例代码如下:

    @RunWith(SpringJunit4ClassRunner.class)
    @ContextConfiguration(locations="classpath:applicationContext.xml")
    public calss TestStudentService{
    	@Autowired
    	private StudentMapper studentMapper;
    	@Test
    	public void getStudent(){
    		studentMapper.getStudent();
    	}
    }
    
  • 相关阅读:
    解决Python开发中,Pycharm中无法使用中文输入法问题
    PointNet++作者的视频讲解文字版
    StringBuilder
    DropDownList 添加“请选择”
    SQLServer 2008中SQL增强之三 Merge(在一条语句中使用
    ASP.NET Web Forms 的 DI 應用範例
    怎么设置环境变量
    ParameterizedThreadStart,ThreadStart的使用,线程Thread传参数
    异步删除
    SqlCommandBuilder的作用
  • 原文地址:https://www.cnblogs.com/liuyi6/p/10217108.html
Copyright © 2020-2023  润新知