Lxxxxxxy_
Lxxxxxxy_
Published on 2019-10-17 / 48 Visits
0

spring注解驱动学习笔记

Spring注解驱动学习笔记

2019年10月14日11:29:36,老师:雷丰阳


DI依赖注入

1、自定义Bean(@Configuration,@Bean)

新建一个Config.java文件,加上注解@Configuration,表明这个类是Spring的配置类。

新建一个Person类:

package cn.lixingyu.springdriver.entity;

/**
 * @author Rlxy93
 * @time 2019/10/14 11:10
 */
public class Person {
    private Integer id;
    private String name;

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Person(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Person() {
    }
}

在文件中添加一个方法,在方法上方加上@Bean,表明这个方法将添加进IOC容器。

	@Bean(value = "lxy")
    public Person person(){
        return new Person(1,"Rlxy93");
    }

在测试方法中测试:

	@Test
    public void test1(){
        ApplicationContext Bean = new AnnotationConfigApplicationContext(Config.class);
        Person person = (Person) Bean.getBean("lxy");
        System.out.println(person);
    }

最终输出:

1571024104301

自定义一个简单的Bean完成。


2、配置扫描器(@ComponentScan)

在Config类中加入注解:

@ComponentScan(value = {"cn.lixingyu.springdriver.controller",
        "cn.lixingyu.springdriver.service","cn.lixingyu.springdriver.repository"},
        includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),
        excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class),useDefaultFilters = false)

其中:

value:扫描哪个包下的文件。

includeFilters:包含哪个类。加上Controller.class表示只扫描有Controller注解的类,但必须加上useDefaultFilters = false这个配置,否则不生效。

type:按照什么方式。

FilterType.ANNOTATION:按照注解。
FilterType.ASSIGNABLE_TYPE:按照类。
FilterType.ASPECTJ:按照ASPECTJ表达式。
FilterType.REGEX:按照正则表达式。
FilterType.CUSTOM:自定义Filter。需要实现TypeFilter。

excludeFilters:排除哪个类。


最终运行效果:

1571024752424


3、设置组件作用域(@Scope)

IOC容器中,组件默认是单例的(无论获取多少次,都是同一个对象)

singleton:单例。IOC容器启动时,调用Bean的方法创造对象放到IOC容器中。
prototype:多例。IOC启动时不会调用Bean的方法,在使用时才会调用,使用几次调用几次。

可以在Bean配置上方加入@Scope来设置作用域。

4、Bean懒加载(@Lazy)

懒加载只针对单例的Bean。

懒加载:容器启动时不创建对象,第一次使用时才创建对象,放入IOC容器中,由于是单例的Bean,所以无法创建多次。

5、按照条件注册Bean(@Conditional)

新建一个类,实现Condition接口,实现matches方法。

示例:判断当前操作系统,如果是windows,则把Person(1,"bill");添加进IOC容器,如果是linux,则把Person(1,"linus");添加进IOC容器。

WindowsCondition:实现Condition接口,添加方法matches。

package cn.lixingyu.springdriver.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @author Rlxy93
 * @time 2019/10/14 17:19
 */
public class WindowsCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        System.out.println(property);
        if(property.contains("Windows")){
            return true;
        }
        return false;
    }
}

LinuxCondition:同上。

package cn.lixingyu.springdriver.condition;


import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;

/**
 * @author Rlxy93
 * @time 2019/10/14 17:20
 */
public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        Environment environment = conditionContext.getEnvironment();
        String property = environment.getProperty("os.name");
        System.out.println(property);
        if(property.contains("linux")){
            return true;
        }
        return false;
    }
}

在Config中加入

    @Bean
    @Conditional(WindowsCondition.class)
    public Person bill(){
        return new Person(1,"bill");
    }

    @Bean
    @Conditional(LinuxCondition.class)
    public Person linus(){
        return new Person(1,"linus");
    }

运行结果:

QQ截图20191014172841


6、给容器中快速添加组件(@Import,实现ImportSelector接口)

给容器注册组件:

1、包扫描+组件标注注解(@Controller,@Service,@Repository,@Component)。

2、@Bean,普通方式,直接返回一个实例化的对象。

3、@Import,直接加上对象.class。

4、实现ImportSelector接口,实现selectImports方法,加入要导入的类的全类名。

新建一个MyImportSelector类

package cn.lixingyu.springdriver.imports;

import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author Rlxy93
 * @time 2019/10/15 10:29
 */
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        String[] classes = new String[]{"cn.lixingyu.springdriver.classes.One",
                "cn.lixingyu.springdriver.classes.Two",
                "cn.lixingyu.springdriver.classes.Three"};
        return classes;
    }
}

在Config类加上@Import(MySelectImports.class)注解即可。

运行结果:

QQ截图20191015104351


5、实现ImportBeanDefinitionRegistrar接口。

新建一个MyImportBeanDefinitionRegisterar类

package cn.lixingyu.springdriver.imports;

import cn.lixingyu.springdriver.classes.Three;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

/**
 * @author Rlxy93
 * @time 2019/10/15 10:44
 */
public class MyImportBeanDefinitionRegisterar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry){
        BeanDefinition one = beanDefinitionRegistry.getBeanDefinition("cn.lixingyu.springdriver.classes.One");
        BeanDefinition two = beanDefinitionRegistry.getBeanDefinition("cn.lixingyu.springdriver.classes.Two");
        if(one!=null && two!=null){
            RootBeanDefinition three = new RootBeanDefinition(Three.class);
            beanDefinitionRegistry.registerBeanDefinition("cn.lixingyu.springdriver.classes.three",three);
        }
    }
}

在Config类加上@Import({MyImportSelector.class,MyImportBeanDefinitionRegisterar.class})注解即可。

上述代码的意思是:如果在IOC容器中,存在One和Two的Bean,才会把Three添加进IOC容器中,否则不添加。

运行效果:

QQ截图20191015105246


6、实现FactoryBean(这是一个工厂Bean)。

​ 1)isSingleton方法中,返回true表示单例,返回false表示多例。

​ 2)默认获取的是工厂Bean调用getObject()方法创建的对象。

​ 3)在Bean前加上&可以获取工厂Bean本身。

新建一个MyFactoryBean类

package cn.lixingyu.springdriver.Factory;

import cn.lixingyu.springdriver.entity.Person;
import org.springframework.beans.factory.FactoryBean;

/**
 * @author Rlxy93
 * @time 2019/10/15 10:53
 */
public class MyFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        System.out.println("执行了getObject方法....");
        return new Person(1,"Rlxy93");
    }

    @Override
    public Class<?> getObjectType() {
        return Person.class;
    }

    @Override
    public boolean isSingleton() {
        //true为单例,false为多例
        return false;
    }
}

需要在Config类中加入

    @Bean
    public MyFactoryBean myFactoryBean(){
        return new MyFactoryBean();
    }

运行效果:

QQ截图20191015110657


7、Bean的生命周期

Bean的声明周期包含创建、初始化和销毁。Bean的生命周期由IOC容器管理,由IOC容器调用相应的Init(),Destory()方法。

1、在@Bean中配置initmethod和destorymethod即可配置好相应的初始化方法和销毁方法。

​ 1)ps:多例的Bean不会自动销毁,容器不会管理这个Bean。

在Person类中加入

        public void init(){
            System.out.println("init....");
        }

        public void destory(){
            System.out.println("destory....");
        }

在Config类中加入

@Bean(initMethod = "init",destroyMethod = "destory")
    public Person person2(){
        return new Person(1,"Rlxy93");
    }

在@Bean中添加initMethod和destoryMethod,对应Person中的init和destory方法。

运行效果:

QQ截图20191015111338


2、实现InitializingBean接口(初始化),DisposableBean接口(销毁)。

在实体类中实现InitializingBean和DisposableBean接口

    package cn.lixingyu.springdriver.entity;

    import org.springframework.beans.factory.DisposableBean;
    import org.springframework.beans.factory.InitializingBean;

    /**
     * @author Rlxy93
     * @time 2019/10/14 11:10
     */
    public class Person implements InitializingBean, DisposableBean {
        private Integer id;
        private String name;

        @Override
        public String toString() {
            return "Person{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }

        public Integer getId() {
            return id;
        }

        public void setId(Integer id) {
            this.id = id;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Person(Integer id, String name) {
            this.id = id;
            this.name = name;
        }

        public Person() {
        }

        @Override
        public void destroy() throws Exception {
            System.out.println("destory....");
        }

        @Override
        public void afterPropertiesSet() throws Exception {
            System.out.println("init....");
        }
    }

在Config中添加

	@Bean
    public Person person2(){
        return new Person(1,"Rlxy93");
    }

ps:不需要在@Bean中写上initMethod和destroyMethod对应的方法名。

运行效果:

QQ截图20191015112252


3、使用JSR250的注解:

​ 1)@PostConstruct,在Bean创建完成并赋值后进行初始化。

​ 2)@PreDestory,在销毁Bean之前通知我们进行清理工作。

在实体类中加上

		@PostConstruct
        public void init(){
            System.out.println("init....");
        }

        @PreDestroy
        public void destory(){
            System.out.println("destory....");
        }

在Config类中加上

	@Bean
    public Person person2(){
        return new Person(1,"Rlxy93");
    }

运行效果:

QQ截图20191015113227


4、实现BeanPostProcessor接口(Bean的后置处理器)

​ 1)Before:在初始化之前执行的方法。

​ 2)After:在初始化之后执行的方法。

新建一个MyBeanPostProcessor类

package cn.lixingyu.springdriver.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;

/**
 * @author Rlxy93
 * @time 2019/10/15 11:34
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行了postProcessBeforeInitialization方法...."+beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("执行了postProcessAfterInitialization方法...."+beanName);
        return bean;
    }
}

ps:

​ 1)在类上要添加@Component注解。

​ 2)要使用@ComponentScan("cn.lixingyu.springdriver.bean")扫描添加了@Component注解的包。

运行效果:

QQ截图20191015115002


8、赋值(@Value)

1、基本赋值。

2、SpEL表达式,#{}(常用的表达式,如1+1,2)。

3、使用外部配置文件,使用${}取配置文件中的值。

​ 1)使用@PropertySource加载外部文件。其中Value属性写 value="classpath:xxx.properties"

在application.properties中加上

name=lxy
age=20

在实体类中加上

        @Value("${age}")
        private Integer id;
        @Value("${name}")
        private String name;

在Config类中加上

	@Bean
    public Person person3(){
        return new Person();
    }

运行效果:

QQ截图20191015115758


9、自动装配(@Autowired,@Qualifier,@Primary)

1、@Autowired,默认优先按照类型装配,如果存在多个同类型的Bean,再按照变量名作为Bean的id去寻找。

优先按照类型匹配

QQ截图20191015120544

按照id匹配

QQ截图20191015121442

ps:首先获取在IOC容器中创建好的两个对象,分别打印它的地址,再获取personService里面的使用了@Autowired注解的personRepository,两个地址一样,说明是按照id匹配的。


2、@Qualifier,指定一个id,按照Bean的id去寻找。

QQ截图20191015122146

ps:

​ 1)需要和@Autowired配合使用。

​ 2)这里给定id是personRepository1,则添加到personService的是personRepository1的地址。


3、@Primary(在Bean上加这个注解destroy设定首选的Bean。

QQ截图20191015122402

ps:这里设定了personRepository1为首选的Bean,所以与下面代码的变量名无关。

	@Autowired
    public PersonRepository personRepository;

4、@Resource,默认是按照变量名查找Bean。

QQ截图20191016091004

使用@Resource注解时,按照变量名查找Bean。


5、@Inject,和@Autowired的功能一样。

10、方法、构造器的自动装配

@Autowired标注在方法上时,Spring会从IOC容器中,查找方法参数里面的对象,把它取出来,完成赋值。

QQ截图20191016091246


@Autowired标注在构造器中时,也是在容器中获取,完成赋值。(必须是有参构造器)

QQ截图20191016091424


@Autowired标注在参数位置时,同样是在容器中获取,完成赋值。

QQ截图20191016091550


@Bean标注的方法创建对象时,方法参数的值从容器中获取。

QQ截图20191016092102


ps:只有一个有参构造器时,可以不用使用@Autowired,还是可以完成赋值。

11、Spring底层组件(实现Aware接口)

1、EmbeddedValueResolverAware接口

新建一个MyEmbeddedValueResolverAware类

package cn.lixingyu.springdriver.Aware;

import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringValueResolver;

/**
 * @author Rlxy93
 * @time 2019/10/16 09:24
 */
@Component
public class MyEmbeddedValueResolverAware implements EmbeddedValueResolverAware {
    @Override
    public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
        String s = stringValueResolver.resolveStringValue("hello ${os.name},i'm #{90*4}");
        System.out.println(s);
    }
}

在Config类中加入"cn.lixingyu.springdriver.Aware"。扫描这个包。

运行效果:

QQ截图20191016092811


2、BeanNameAware接口

新建一个MyBeanNameAware类

package cn.lixingyu.springdriver.Aware;

import org.springframework.beans.factory.BeanNameAware;
import org.springframework.stereotype.Component;

/**
 * @author Rlxy93
 * @time 2019/10/16 09:30
 */
@Component
public class MyBeanNameAware implements BeanNameAware {
    @Override
    public void setBeanName(String s) {
        System.out.println("当前Bean的名字"+s);
    }
}

运行结果:

QQ截图20191016093202


12、Profile环境搭建(@Profile)

Profile:Spring为我们提供的可以根据当前环境,动态地激活、切换一系列组件的功能。

比如:有开发环境、测试环境和生产环境,可以动态的切换A数据库、B数据库和C数据库。

@Profile:里面加定义的环境标识,如:test,dev,默认是default。不加的时候,在所有环境都会生效。

新建一个MyDataSource类

package cn.lixingyu.springdriver.bean;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;

/**
 * @author Rlxy93
 * @time 2019/10/16 09:33
 */
@Configuration
@PropertySource("classpath:application.properties")
public class MyDataSource {

    @Value("${jdbc.user}")
    private String user;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.driver}")
    private String driver;
    private ComboPooledDataSource dataSource;
    {
        dataSource = new ComboPooledDataSource();
    }

    @Profile("test")
    @Bean
    public DataSource test() throws PropertyVetoException {
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl("jdbc:mysql:///shiro");
        return dataSource;
    }

    @Profile("dev")
    @Bean
    public DataSource dev() throws PropertyVetoException {
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl("jdbc:mysql:///shop");
        return dataSource;
    }

    @Profile("prod")
    @Bean
    public DataSource prod() throws PropertyVetoException {
        dataSource.setUser(user);
        dataSource.setPassword(password);
        dataSource.setDriverClass(driver);
        dataSource.setJdbcUrl("jdbc:mysql:///spring");
        return dataSource;
    }

}

如何切换环境?:

​ 1、使用命令行参数,-Dspring.profiles.active=test,表示当前环境是test环境。

​ 运行结果:

QQ截图20191016094439


​ 2、使用代码

        //使用无参构造器创建一个IOC容器
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        //设置需要激活的环境
        ac.getEnvironment().addActiveProfile("dev");
        //注册配置类
        ac.register(MyDataSource.class);
        //启动刷新容器
        ac.refresh();
        String[] names = ac.getBeanDefinitionNames();
        for(String name : names){
            System.out.println(name);
        }

​ 运行结果:

QQ截图20191016094820


AOP切面

AOP切面初识(@AspectJ,@EnableAspectJAutoProxy,@Before,@After,@AfterReturning,@AfterThrowing,@Around,@PointCut)

通知方法:

​ 前置通知:@Before,在方法执行前调用。

​ 后置通知:@After,在方法执行之后调用。

​ 返回通知:@AfterReturning,返回方法调用后的返回值。

​ 异常通知:@AfterThrowing,方法异常时调用。

环绕通知:@Around,手动推进目标方法运行,joinPoint.procced();执行目标方法。

1、将业务逻辑类和切面类添加进容器中。

2、告诉Spring哪个是切面类@AspectJ。

3、在Config类上加入@EnableAspectJAutoProxy。

4、如果要获取方法名和参数列表,则可以在方法参数加上JoinPoint joinPoint。其中,返回通知则加Object result,异常通知则加Exception exception。

新建一个MyAopMethod类

package cn.lixingyu.springdriver.bean;

/**
 * @author Rlxy93
 * @time 2019/10/17 10:13
 */
public class MyAopMethod {

    public int div(int i, int j){
        System.out.println(i/j);
        return i/j;
    }

}

新建一个MyAop类

package cn.lixingyu.springdriver.Aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * @author Rlxy93
 * @time 2019/10/17 10:10
 */
@Aspect
@Component
public class MyAop {

    @Pointcut("execution(public int cn.lixingyu.springdriver.bean.MyAopMethod.div(int,int))")
    public void pointCut() {
    }

    @Before("pointCut()")
    public void before() {
        System.out.println("Before....");
    }

    @AfterReturning(value = "pointCut()", returning = "result")
    public void returning(JoinPoint joinPoint, int result) {
        System.out.println("Returning=" + result+","+joinPoint.getArgs()[0]+joinPoint.getArgs()[1]);
    }

    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void exception(JoinPoint joinPoint, Exception e) {
        System.out.println("Exception=" + e.getMessage());
    }

    @After("pointCut()")
    public void after(JoinPoint joinpoint) {
        System.out.println("After...." + joinpoint.getSignature().getName());
    }

}

在Config类中加入

@Bean
    public MyAopMethod myAopMethod(){
        return new MyAopMethod();
    }

    @Bean
    public MyAop myAop(){
        return new MyAop();
    }

ps:joinPoint参数必须要出现在参数列表的第一位,否则会出错。

运行效果:

QQ截图20191017103127


声明式事务

开启基于注解的事务(@EnableTransactionManagement)

1、在Config类中配置事务管理器,返回一个事务管理器,必须把dataSource注册在事务管理器中。

2、在Config类加上@EnableTransactionManagement。

3、在业务逻辑方法上加@Transaction。