• Spring 常用注解


    1.组件注解
    @Component("xxx")
    @Scope("prototype")
    @Lazy(true)
    @DepondsOn({"aa","bb"})
    @PostConstructor和@PreDestroy
    @Resource(name="xx")
    @Autowired(required=false)


    2.Aop相关注解
    @Aspect
    @Before
    @AfterReturning
    @AfterThrowing
    @After
    @Around
    @Pointcut

    3.Java配置类相关注解
    @Bean(name="xxx")
    @ConfigurationProperties
    @Configuration
    @Import
    @ImportResource
    @Value("${expression}")
    @PropertySource(value="classpath:jdbc.properties")


    4.SpringMVC 相关注解
    @ResponseBody
    @RestController
    @RequestBody
    @RequestParam

    下面我们一个一个的详细说说以上注解怎么使用(请仔细阅读以下通俗语言归纳,句句“诛心”):

    组件注解 :

    @Component(“xxx”)
    指定某个类是容器的bean,@Component(value="xx")相当于<bean id="xx" />,其中value可以不写。

    用于标注类为spring容器bean的注解有四个,主要用于区别不同的组件类,提高代码的可读性。

    • @Component, 用于标注一个普通的bean
    • @Controller 用于标注一个控制器类(控制层 controller)
    • @Service 用于标注业务逻辑类(业务逻辑层 service)
    • @Repository 用于标注DAO数据访问类 (数据访问层 dao)

    对于上面四种注解的解析可能是相同的,尽量使用不同的注解提高代码可读性。

    注解用于修饰类,当不写value属性值时,默认值为类名首字母小写。

    @Scope(“prototype”)

    该注解和@Component这一类注解联合使用,用于标记该类的作用域,默认singleton
    也可以和@Bean一起使用,此时@Scope修饰一个方法。关于@Bean稍后有说明

    @Lazy(true)

    指定bean是否延时初始化,相当于<bean id="xx" lazy-init=""> ,默认false。@Lazy可以和@Component这一类注解联合使用修饰类,也可以和@Bean一起使用修饰方法

    注:此处初始化不是指不执行init-method,而是不创建bean实例和依赖注入。只有当该bean(被@Lazy修饰的类或方法)被其他bean引用(可以是自动注入的方式)或者执行getBean方法获取,才会真正的创建该bean实例,其实这也是BeanFactory的执行方式。

    @DepondsOn({“aa”,“bb”})

    该注解也是配合@Component这类注解使用,用于强制初始化其他bean

    @DepondsOn("other")
    @Lazy(true)
    @Controller
    @Scope("prototype")
    public class UserAction{
    	String wdnmd = "我带你们打";
    }
    

    上面的代码指定,初始化bean “userAction"之前需要先初始化“aa”和“bb”两个bean,但是使用了@Lazy(true)所以spring容器初始化时不会初始化"userAction” bean。

    @PostConstructor和@PreDestroy

    @PostConstructor和@PreDestroy这两个注解是jee规范下的注解。这两个注解用于修饰方法,spring用这两个注解管理容器中spring生命周期行为。

    • @PostConstructor 从名字可以看出构造器之后调用,相当于<bean init-method="">。就是在依赖注入之后执行
    • @PreDestroy 容器销毁之前bean调用的方法,相当于<bean destroy-method="">

    @Resource(name=“xx”)注意这个注解要导入相关jar,请自行百度

    @Resource 可以修饰成员变量也可以修饰set方法。当修饰成员变量时可以不写set方法,此时spring会直接使用jee规范的Field注入。
    @Resource有两个比较重要的属性,name和type

    • 如果指定了name和type,则从Spring容器中找到唯一匹配的bean进行装配,找不到则抛出异常;
    • 如果指定了name,则从spring容器查找名称(id)匹配的bean进行装配,找不到则抛出异常;
    • 如果指定了type,则从spring容器中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常;
    • 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配

    如果没有写name属性值时

    • 修饰成员变量,此时name为成员变量名称
    • 修饰set方法,此时name 为set方法的去掉set后首字母小写得到的字符串

    @Autowired(required=false)

    @Autowired可以修饰构造器,成员变量,set方法,普通方法。@Autowired默认使用byType方式自动装配。required标记该类型的bean是否时必须的,默认为必须存在(true)。
    可以配合@Qualifier(value="xx"),实现按beanName注入。

    • required=true(默认),为true时,从spring容器查找和指定类型匹配的bean,匹配不到或匹配多个则抛出异常
    • 使用@Qualifier("xx"),则会从spring容器匹配类型和 id 一致的bean,匹配不到则抛出异常

    @Autowired会根据修饰的成员选取不同的类型

    • 修饰成员变量。该类型为成员变量类型
    • 修饰方法,构造器。注入类型为参数的数据类型,当然可以有多个参数

    以下是个小DEMO 

    业务逻辑层

    //service层,业务逻辑
    @Service
    public class UserService{
    	
    	@Resource(name="userDao")
    	private UserDao userDao;
    	//@Autowired
    	//@Qualifier("userDao")
    	//private IUserDao userDao;
    	//相对来说使用`@Resource`更简单一些
    	.......实际业务.............
    }
    

     数据访问层

    //dao层,持久化
    @Repository
    @Lazy(true)
    @Scope("singleton")
    public class UserDao implements InitializingBean,DisposableBean{
    	public UserDao() {
    		System.out.println("constructor...................");
    	}
    	
    	public List<User> listUsers(){
    		System.out.println("查询所有用户");
    		return null;
    	}
    	@PostConstruct
    	public void postConstructor() {
    		System.out.println("post_constructor.................");
    	}
    	
    	//覆盖InitializingBean接口方法
    	@Override
    	public void afterPropertiesSet() throws Exception {
    		System.out.println("after_properties_set..............");
    		
    	}
    	
    	@PreDestroy
    	public void after() {
    		System.out.println("pre_destroty.................");
    	}
    	//重写DisposableBean方法
    	@Override
    	public void destroy() throws Exception {
    		System.out.println("destry_method.............");
    	}
    }
    

    测试类

    //测试类
    public class TestAnnotation{
    	@Test 
    	public void test1(){
    		ClassPathXmlApplicationContext application=new ClassPathXmlApplicationContext("applicationContext.xml");
    		//配置文件里只有一行就是开启自动扫描” <context:component-scan base-package="com" /> “
    		System.out.println();
    		System.out.println("--------获取bean-----------");
    		System.out.println();
    		System.out.println(application.getBean("userDao"));
    		//关闭容器,就会销毁容器中的bean
    		application.close();
    	}
    }
    

     输出结果

    constructor...................
    post_constructor.................
    after_properties_set..............
    
    --------获取bean-----------
    
    com.dao.PersonManager@4872669f
    pre_destroty.................
    destry.............
    pre_destroty.................
    destry.............
    

    可以看到虽然UserDao 使用@Lazy,但是还是在spring容器初始化的时候还是创建了UserDao实例。原因很简单,因为在UserService中需要注入UserDao,所以在此时创建的UserDao实例也属于延时初始化。

    在上面我们还使用了两个接口InitializingBean 和DisposableBean,这两个接口用于管理singleton作用域的bean的生命周期,类似init-method和destroy-method。不同之处就是调用的循序不一致。

    初始化调用顺序 @PostConstructor > InitializingBean > init-method 用于指定bean依赖注入后的行为,
    销毁调用顺序 @PreDestroy > DisposableBean > destroy-method 用于定制bean销毁之前的行为。

    Aop相关注解:

    该注解是AspectJ中的注解,并不是spring提供的,所以还需要导入aspectjweaver.jar,aspectjrt.jar,除此之外还需要依赖aopalliance.jar。

    <dependency>
         <groupId>aopalliance</groupId>
         <artifactId>aopalliance</artifactId>
         <version>1.0</version>
     </dependency>
    
     <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjweaver</artifactId>
         <version>1.9.2</version>
     </dependency>
    
     <dependency>
         <groupId>org.aspectj</groupId>
         <artifactId>aspectjrt</artifactId>
         <version>1.5.4</version>
     </dependency>
    

    UserDao.java

    package com.example.aop;
    
    import com.example.domain.User;
    import org.springframework.stereotype.Repository;
    import java.util.ArrayList;
    import java.util.List;
    
    @Repository
    public class UserDao {
        public UserDao() {
            System.out.println("constructor...................");
        }
    
        public List<User> listUsers() {
            System.out.println("查询所有用户");
            return new ArrayList();
        }
    }
    

    配置文件 applicationContext.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"  
        xmlns:context="http://www.springframework.org/schema/context"  
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:aop="http://www.springframework.org/schema/aop"   
        xsi:schemaLocation="  
            http://www.springframework.org/schema/beans       
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd  
            http://www.springframework.org/schema/context   
            http://www.springframework.org/schema/context/spring-context-3.0.xsd  
            http://www.springframework.org/schema/aop  
            http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">  
        <context:component-scan base-package="com" />  <!--自动扫描-->
    	<aop:aspectj-autoproxy></aop:aspectj-autoproxy><!--开启@AspectJ支持-->
    </beans> 
    

    测试类

    public class TestAnnotation{
        @Test 
        public void test1(){
            ClassPathXmlApplicationContext application=new ClassPathXmlApplicationContext("applicationContext.xml");
            //配置文件里只有一行就是开启自动扫描” <context:component-scan base-package="com" /> “
           UserDao userDao = application.getBean("userDao",UserDao.class);
           System.out.println("返回结果:	"+userDao.listUsers());
        }
    } 

     @Aspect

    修饰Java类,指定该类为切面类。当spring容器检测到某个bean被@Aspect修饰时,spring容器不会对该bean做增强处理(bean后处理器增强,代理增强)

    package com.example.aop;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    public class UserAdvice{
    
    }
    

     @Before

    修饰方法,before增强处理。。用于对目标方法(切入点表达式表示方法)执行前做增强处理。可以用于权限检查,登陆检查
    常用属性

    • value:指定切入点表达式 或者引用一个切入点

    对com.example.aop 包下所有的类的所有方法做 before增强处理

    @Before(value = "execution(* com.example.aop.*(..)))")
    public void before(JoinPoint joinPoint){
            System.out.println("before  增强处理。。。。。。。。。。。。");
    }
    

     结果:

    constructor…
    before 增强处理。。。。。。。。。。。。
    查询所有用户
    返回结果: []
    

     如果同一条切入点表达式被使用多次,可以使用更友好的方式。定义一个切入点

     @Pointcut("execution(* com.example.aop.*(..)))")
     public void addLog(){}
    
    @Before(value = "addLog()")
    public void before(JoinPoint joinPoint){
            System.out.println("before  增强处理。。。。。。。。。。。。");
    }
    

     增强方法可以接受一个JoinPoint 类型的参数,用于获取被执行目标方法的一下属性。

    @Before(value = "addLog()")
        public void execute(JoinPoint joinPoint){
            System.out.println("before  增强处理。。。。。。。。。。。。");
            System.out.println(Arrays.toString(joinPoint.getArgs()));
            System.out.println(joinPoint.getKind());
            System.out.println(joinPoint.getSignature().getName());
            System.out.println(joinPoint.getSignature().getDeclaringTypeName());
            System.out.println(joinPoint.getSignature().getDeclaringType());
            System.out.println(joinPoint.getSignature().getModifiers());
            System.out.println(joinPoint.getSourceLocation());
            System.out.println(joinPoint.getTarget());
            System.out.println(joinPoint.getThis());
        }
    

     结果:

    constructor…
    before 增强处理。。。。。。。。。。。。
    []
    method-execution
    listUsers
    com.example.aop.UserDao
    class com.example.aop.UserDao
    1
    org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint$SourceLocationImpl@15d49048
    com.example.aop.UserDao@7098b907
    com.example.aop.UserDao@7098b907
    查询所有用户 

    @AfterReturning
    修饰方法,afterreturning增强处理。目标方法正常结束后做增强处理。

    常用属性

    pointcut/value:指定切入点表达式
    returning:指定一个参数名,用于接受目标方法正常结束时返回的值。参数名称需要在增强方法中定义同名的参数。

    注意:

    • 如果使用了returning 。那么增强方法中的数据类型必须是返回结果的类型或者父类型,否则不会调用该增强处理。
    • 使用了returning 还可以用来修改返回结果。

    以上面的例子来说,目标方法返回结果类型应该满足下面的条件

    returnResult instanceof List
    

     修改返回值

    @AfterReturning(pointcut = "addLog()",returning = "list")
    public void afterReturning(JoinPoint joinPoint,List list){
         System.out.println("afterReturning..............");
         System.out.println("afterReturning  接收结果。。。。。" + list);
         //修改返回结果
         list.add(new User(3L, "afterReturning......"));
    }
    

    可以看到 AfterReturning 修改了返回结果。

    @AfterThrowing
    修饰方法,afterthrowing增强处理。当目标程序方法抛出 异常或者异常无法捕获时,做增强处理。

    常用属性

    pointcut/value :指定切入点表达式
    throwing:指定一个形参,在增强方法中定义同名形参,用于访问目标方法抛出的异常

        @AfterThrowing(pointcut = "addLog()",throwing = "e")
        public void afterThrowing(JoinPoint joinPoint,Exception e){
            System.out.println("afterThrowing..............");
            System.out.println("抛出异常。。。。。"+e);
        }
    

     参数类型必须是 Throwable 的子类,同样也会有上面@AfterReturning 参数类型匹配的问题。

    @After

    修饰方法 ,after增强处理。无论方法是否正常结束,都会调用该增强处理(@After= @AfterReturning+@AfterThrowing)。

    但是该增强方式无法获取目标方法的返回结果,也获取目标方法抛出的异常。所以一般用于进行释放资源,功能类似于 finally

    常用属性

    value :指定切入点表达式

     @After("addLog()")
     public Object after(JoinPoint joinPoint) {
          System.out.println("after..............");
    		
    	 //试图修改返回结果
          List<User> list = new ArrayList<>();
          list.add(new User(1L, "after....."));
          return list;
      }
    

     结果:

    constructor…
    before 增强处理。。。。。。。。。。。。
    查询所有用户
    after…
    afterReturning…
    afterReturning 接收结果。。。。。[]
    查询结果: [User{id=3, name=‘afterReturning…’}]
    

     从上面的结果来看 After 增加处理 ,因为不能接受返回结果作为参数,所以不能修改返回结果。

    @Around

    修饰方法, around增强处理。该处理可以目标方法执行之前和执行之后织入增强处理(@Before+@AfterReturning)。
    Around增强处理通常需要在线程安全的环境下使用,如果@Before和@AfterReturning可以处理就没必要使用@Around。

    常用属性

    value :指定切入点表达式
    当定义一个Aound增前处理时,增强方法第一形参需要时ProceedingJoinPoint类型。ProceedingJoinPoint有一个Object proceed()方法,用于执行目标方法。当然也可以为目标方法传递数组参数,来修改目前方法的传入参数。

    public interface ProceedingJoinPoint extends JoinPoint {
        void set$AroundClosure(AroundClosure var1);
        
        Object proceed() throws Throwable;	
    	/**
    	* 修改目标方法的传参
    	*/
        Object proceed(Object[] var1) throws Throwable;
    }
    

     小结:

    • Around增强处理通常需要在线程安全的环境下使用
    • 调用 proceed()可以获取返回结果,所以可以修改目标方法的返回值
    • proceed(Object[] var1) 可以修改入参,修改目标方法的入参
    • 可以进行目标方法执行之前和执行之后织入增强处理

    使用Around 修改返回结果:

    @Around(value= "addLog()")
    public Object around(ProceedingJoinPoint joinPoint){
           System.out.println("around.....begin");
           Object object=null;
           try {
               object = joinPoint.proceed();
    			//修改返回结果
               List<User> list = (List)object;
               list.add(new User(4L,"around...."));
               object=list;
           } catch (Throwable throwable) {
               System.out.println("exception....");
           }
           System.out.println("around.....end");
           return object;
      }
    

     结果:

    constructor…
    around…begin
    before 增强处理。。。。。。。。。。。。
    查询所有用户
    around…end
    after…
    afterReturning…
    afterReturning 接收结果。。。。。[User{id=4, name=‘around…’}]
    查询结果: [User{id=4, name=‘around…’}, User{id=3, name=‘afterReturning…’}]
    

     可以看到 around 和 afterReturning 都可以修改返回结果。不过两者的原理不同。

    • around:可以任意修改,或者返回不相关的值。这个返回值完全可以自主控制
    • afterReturning,通过方法参数 ,使用对象引用的方式来修改对象。修改对象引用地址那么修改时无效的
    [User{id=4, name=‘around…’}, User{id=3, name=‘afterReturning…’}]
    

     从此之外从输出结果来看,增强处理是有序的

     

    小结:

    只有 around 和 afterReturning 可以获取并修改返回结果。需要注意两种方式修改的区别。

    around 需要线程安全

    虽然增强处理都需要 切入点表达式,并不是都支持 pointcut 属性,所以最好都是用value 属性指定。当注解只需要value属性时,value可以省略

    @Before(value = "execution(* com.example.aop.*(..)))")
    @Before("execution(* com.example.aop.*(..)))")
    

     @Pointcut

    修饰方法,定义一个切入点表达式用于被其他增强调用。使用该方式定义切入点方便管理,易复用。

    切入点方法定义和测试方法定义类似,具有以下特点:

    • 无返回值 (void)
    • 无参数
    • 方法体为空
    • 方法名就是切入点名称
    • 方法名不能为 execution
    @Pointcut("execution(* user*(..))")
    //使用一个返回值为void,空方法体的方法命名切入点。
    //public 为修饰符,跟方法的修饰符一致,public 可以在其他切面类中使用该切点,default在同一个包下的切面类中使用该切点
    //返回值必须为void , 方法名就是定义的切点名称
    public  void userAdvice(){}
    

    切入点表达式

    切入点表达式可以通过 &&、 ||、 ! 连接

    1. execution 表达式格式:

    execution(modifiers-pattern? return-type-pattern declaring-type-pattern? method-name(param-pattern) throws-pattern?)
    

     

    注:?表示出现一次或零次

    1. modifers-pattern:方法的修饰符,支持通配符,可以省略
    2. return-type-pattern ;方法返回值类型,必写可以使用通配符*,表示所有返回值类型
    3. declaring-type-pattern:类路径。可以省略表示所有类。可以使用通配符。 com.example.dao.User* //表示 匹配com.example.dao包下,所有以User开头的类
    4. method-name: 方法名,必写。可以使用通配符*,表示所有方法。
    5. param-pattern:方法参数,必写。

    使用通配符* ,表示任意一个参数类型。
    使用通配符==…==,表示零个或多个任意类型的参数。
    (*,String),匹配两个参数的方法。表示第一个参数类型任意,第二个必须为String类型。
    (…),表示任意(个数、类型)参数
    throws-pattern:方法抛出异常,可以省略

    execution(* *(..)) //  匹配所有包及其子包下所有类的所有方法
    execution(* com..*(..)) //  匹配com包及其子包下所有类的所有方法
    execution(* com.example..*(..)) //  匹配com.example包及其子包下所有类的所有方法
    execution(* com.example..get*(..)) //  匹配com.example包及其子包下所有类的所有get方法

      2.within:

    • 匹配指定类下的所有方法。
    • 匹配执行包及其子包下所有类的所有方法
    within(com.example..*) //匹配com.example包及其子包下所有类的所有方法
    within(com.example.aop.UserDao) //匹配 UserDao类下的所有方法
    

    所以within可以看做execution的简写,不需要指定返回类型、方法名、参数

    within(com..*)
    execution(* com.*(..))
    

    3. @annotation:匹配使用指定注解修饰的目标方法;

    @Before("@annotation(com.example.aop.CustomMethodAnnotation)")
    

     匹配使用@CustomAnnotation注解的目标方法。

    4. @within: 用于匹配使用指定注解修饰的类下的所有方法
    within 作用范围是类,@within的作用范围与其一致。不同的是@within 指定的不是类而是注解

    @Before("@within(org.springframework.web.bind.annotation.ResponseBody)")
    

     匹配使用@ResponseBody 注解的类 下的所有方法。

    新增AOP 测试类:

    package com.example.aop;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    @Component
    @Aspect
    public class AdviceTest{
    
    	//execution
        @Before("execution(* com.example..*(..))")
        public void execution(JoinPoint joinPoint) {
            System.out.println("execution..........");
        }
    
        //within
        @Before("within(com.example..*)")
        public void within(JoinPoint joinPoint) {
            System.out.println("within..........");
        }
        //@within
        @Before("@within(org.springframework.stereotype.Repository)")
        public void withinAnnotation(JoinPoint joinPoint) {
            System.out.println("withinAnnotation..........");
        }
    
        //@annotation
        @Before("@annotation(com.example.aop.CustomMethodAnnotation)")
        public void annotationAnnotation(JoinPoint joinPoint) {
            System.out.println("annotationAnnotation..........");
        }
    }
    

     自定义注解:方法注解

    package com.example.aop;
    
    import java.lang.annotation.*;
    
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface CustomMethodAnnotation {
        String value() default "";
    }
    

     修改类UserDao 对listUsers 增加自定义方法注解。

    @CustomMethodAnnotation
     public List<User> listUsers() {
         System.out.println("查询所有用户");
         return new ArrayList();
     }
    

     输出结果:

    constructor…
    annotationAnnotation…
    execution…
    within…
    withinAnnotation…
    查询所有用户
    []
    

     可以看到不同的声明方式 顺序也是不同的。

    AOP 小结:

    • Around增强处理通常需要在线程安全的环境下使用
    • 使用 around 和 afterReturning 可以获取并修改返回结果
    • 增强处理指定 切入点表达式时,最好使用value 属性
    • 切入点 名称(方法名)不能为 execution
    • AfterReturning 指定了 returning属性接受目标方法返回结果,注意 参数类型需要和返回结果类型一致(满足 resutType instanceof argsType)

    增强方式的顺序:

     切入点表达式不同声明方式的顺序:

    Java配置类相关注解

    @Bean(name=“xxx”)

    修饰方法,该方法的返回值为spring容器中管理的bean。当然该注解和上面的@Component效果一样,主要用于做区分

    @Bean 通常使用在 @Configuration 修饰的配置类中,该注解功能相当于<bean />元素

    常用的属性

    • name:bean id 。name可以省略,省略时bean名称为方法名。也可以指定多个名称(逗号隔开)。
    • autowire: 是否自动注入,默认Autowire.NO
    • initMethod:bean的初始化方法。在依赖注入之后执行<bean id="" init-method="">
    • destroyMethod: spring容器关闭时bean调用的方法 <bean id="" destroty-method="">
    • 当然@Bean还可以配合@Scope指定bean的作用域
    @Bean
    public User user(){
    	return new User();
    }
    

    @ConfigurationProperties

    用于从属性文件中获取值 application.properties 或者 application.yml。当然了 如果在配置文件中引入其他配置文件,也可以获取到属性值。

    # 开发环境的配置文件 application-dev.properties
    # 通常会配置三套, 生产,测试,本地
    # 将通用部分配置存放在 application.yml,譬如 数据库连接等信息存放在application-xxx.yml中。这样不用每次都要繁琐的修改。
    spring.profiles.active=dev
    

     包含的属性:

    • value | prefix 两者互为别名。指定前缀,默认为""
    • ignoreUnknownFields:默认为true。是否忽略未知字段,当实体中的字段在配置文件中不存在时,是忽略还是抛出异常
    • ignoreInvalidFields: 默认false。 是否忽略不合法的字段,此处的不合法是指类型不合适,配置文件中存在改配置但是无法转化为指定的字段类型。

    Mybatis属性配置

    application.properties

    #  配置mybatis 
    mybatis.configuration.mapperLocations=classpath:mybatis/mapper/*.xml
    mybatis.configuration.typeAliasPackage=com.example.domain
    mybatis.configuration.configLocation=classpath:mybatis/mybatis-config.xml
    

    ConfigurationProperties 可以配置前缀,然后会根据实体的变量名拼接前缀,去配置文件中查询配置。

    @Component
    @ConfigurationProperties(prefix="mybatis.configuration")
    @Data
    public class MybatisProperties {
        private String configLocation ; //配置文件的路径等价于  @Value("mybatis.configuration.configLocation")
        private String mapperLocations; //配置Mapper映射文件的路径
        private String typeAliasPackage; //别名的实体路径
    }
     
    

     @Configuration

    修饰一个Java类,被修饰的类相当于一个xml配置文件。功能类似于 <beans> </beans>
    在springboot中大量使用了该注解,该注解提供了一种使用Java类方式配置bean。

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Component
    public @interface Configuration {
        @AliasFor(
            annotation = Component.class
        )
        String value() default "";
    }
    

     可以发现 @Configuration使用了@Component 注解修饰。

    配置 Mybatis 会话工厂

    @Configuration
    @AutoConfigureAfter(DataSourceConfig.class)
    public class MyBatisConfig  {
    
        @Autowired
        private DataSource dataSource;
    
        @Autowired
        MybatisProperties mybatisProperties;
    
        @Bean(name="sqlSessionFactory")
        @Primary
        public SqlSessionFactory getSqlSessionFactory() throws Exception {
            SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
            factoryBean.setDataSource(dataSource); //配置数据源
            factoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasPackage()); //实体类所在包
            factoryBean.setConfigLocation(new DefaultResourceLoader().getResource(mybatisProperties.getConfigLocation()));// mapper配置文件
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(mybatisProperties.getMapperLocations()));  //Mapper实体类所在路径
    
            return factoryBean.getObject();
        }
    
        @Bean
        public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
            return new SqlSessionTemplate(sqlSessionFactory);
        }
    	
    	// 事务管理器
        @Bean
        public PlatformTransactionManager getDatasourceManager(){
            return new DataSourceTransactionManager(dataSource);
        }
    }
    

     @Import

    功能和 <import/> 类似,修饰Java类,用于向当前类导入其他配置类。 可以导入多个配置文件

    通常用于导入不在包扫描范围内的配置文件。可以被扫描的配置类可以直接访问,没有必要使用@Import 导入

    比如 SpringBoot的启动类指定的包扫描路径为 com.example

    @SpringBootApplication
    @ComponentScan("com.example")
    @MapperScan("com.example.mapper")
    public class Application {
    
        public static void main(String[] args) {
            SpringApplication.run(Application.class, args);
        }
    }
    

     数据库的配置文件在 com包下。

    package com;
    
    @Configuration
    public class DataSourceConfig {
    
        @Autowired
        private JdbcProperties properties;
    
        @Bean(name = "dataSource")
        @Primary
        public DataSource getSqlDataSource() throws SQLException {
            DruidDataSource datasource=new DruidDataSource();
           	............................
            return datasource;
        }
    }
    

     在MyBatisConfig 中引入 DataSourceConfig, 就会解析DataSourceConfig。将解析出的Bean交给容器管理

    @Configuration
    @AutoConfigureAfter(DataSourceConfig.class)
    @Import(DataSourceConfig.class)
    public class MyBatisConfig 
    

     @ImportResource

    修饰Java类,用于向类引入xml配置文件。

    用于导入包含bean定义的配置文件,功能和 <import/> 类似。默认情况下可以处理后缀为 .groovy 和.xml 的配置文件、

    @AliasFor("locations")
    String[] value() default {};
    @ImportResource({"classpath:config/beans.xml"})
    

    @Value("${expression}")

    修饰成员变量或者 方法、构造器的参数,用于属性值注入(在配置文件中配置的值)。

    注意:

    • @Value不能对static 属性注入。

    @PropertySource(value=“classpath:jdbc.properties”)
    该注解用来加载属性文件。

    常用属性:

    • ignoreResourceNotFound: 当资源文件找不到的时候是否会忽略该配置,而不是抛出错误。一般用于可选项
    • encoding : 资源文件使用什么编码方式
    • value : 指定属性文件位置。可以配置多个属性文件,不可以使用通配符。

    在 PropertySource 中可以指定多个路径,并且会将属性文件中的值加载到 Environment 中。

    上面介绍一个加载配置文件的注解 @ConfigurationProperties 。
    它们的使用有一些差异:

    1. @PropertySource使用该注解加载的是相对独立的属性文件,可以同时加载多个文件(xxx.properties),而且不支持自动注入,不支持前缀注入
    2. @ConfigurationProperties 用于加载配置文件(application.properties | application.yml)。该注解功能更强大:具体有以下三点
    • 支持前缀注入 (prefix)
    • 相同属性名的自动注入
    • $("") 支持EL表达式注入

    在以往的开发中通常会将数据库连接信息存放在单独的属性文件中(jdbc.properties)。而在spring boot 中我们会将数据库的信息存放在配置文件中,这会极大便利开发工作。

    jdbc.properties

    #数据源配置
    spring.datasource.url=xxxxxxxx
    spring.datasource.username=xxxx
    ..............................
    

    可以通过 @Value 注解将配置文件的值注入到实体类中

    @Configuration
    @PropertySource("classpath:jdbc.properties")
    @Data
    public class JdbcProperties {
        @Value("${spring.datasource.maxActive}")
        private String url;
    
    	@Value("${spring.datasource.maxActive}")
        private String type;
    	..............................
    	
    }
    

     也可以注入Environment ,通过Environment 获取值

       @Configuration
       @PropertySource("classpath:jdbc.properties")
       public class AppConfig {
      
           @Autowired
           Environment env;
      
           @Bean
           public User user() {
               System.out.println(env.getProperty("spring.datasource.url"));
               return new User();
           }
       }
    

    @Value还能注入很多类型,字符串、系统变量等等,参考《JAVAee的颠覆者》

    SpringMVC 相关注解

    @ResponseBody

    控制器方法返回值会使用HttpMessageConverter 进行数据转化。通常会转化为JSON字符串

    同样的 ResponseBodyAdvice: 针对所有以@ResponseBody的参数做处理。

    @RestController

    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Controller
    @ResponseBody
    public @interface RestController
    

     @RestController = @Controller + @ResponseBody

    @RequestBody

    从Reuqest请求体中获取内容,绑定到方法的指定参数上。 SpringMVC 使用HttpMessageConverter 接口将请求体中的数据转化为方法参数类型。

    SpringMVC 给用户对参数的处理提供了很大支配权。 我们可以使用 接口RequestBodyAdvice 来实现对参数进行拦截处理。
    注意:

    • RequestBodyAdvice : 针对所有以@RequestBody的参数做处理
    • 自定义的处理对象类上必须得加上@ControllerAdvice注解!

    利用此功能我们可以做以下处理工作。

    1. 参数做解密处理。
    2. 修改接受的参数数据。

    参数解密功能

    IOUtils 依赖包:

    <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.6</version>
    </dependency>
    

     定义一个注解。用来控制参数是否加密

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface SecurityParameter {
        /**
         * 入参是否解密,默认解密
         */
        boolean inDecode() default true;
        /**
         * 出参是否加密,默认加密
         */
        boolean outEncode() default true;
    }
    

     @RequestBody 修饰参数的处理类

    // 对com.demo.controller 包下的方法进行 处理
    @ControllerAdvice(basePackages = "com.demo.controller")
    public class RequestParameterHandle implements RequestBodyAdvice {
        private static final String ENCODING = "UTF-8";
    
        //1.  控制该处理器是否支持对数据进行处理
        // 主要用于判断该处理器是否对传入的数据类型支持
        @Override
        public boolean supports(MethodParameter methodParameter, Type type,
                                Class<? extends HttpMessageConverter<?>> aClass) {
            return true;
        }
    
        //2. 当supports返回true ,执行该部分代码。在请求体被读取和转化之前执行
        @Override
        public HttpInputMessage beforeBodyRead(HttpInputMessage httpInputMessage,
                                               MethodParameter methodParameter, Type type,
                                               Class<? extends HttpMessageConverter<?>> aClass) throws
                IOException {
            //解码
            boolean decoder = false;
            //判断方法是否使用@SecurityParameter注解
            if (methodParameter.getMethod().isAnnotationPresent(SecurityParameter.class)) {
                //获取注解配置的包含和去除字段
                SecurityParameter serializedField = methodParameter
                        .getMethodAnnotation(SecurityParameter.class);
                //入参是否需要解密
                decoder = serializedField.inDecode();
            }
    
            //自定义解码
            if (decoder) {
                return new CustomerHttpInputMessage(httpInputMessage);
            } else {
                return httpInputMessage;
            }
        }
    
        @Override
        public Object handleEmptyBody(Object o, HttpInputMessage
                httpInputMessage, MethodParameter methodParameter, Type type,
                                      Class<?  extends  HttpMessageConverter<?>> aClass) {
            return o;
        }
    
        @Override
        public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage,
                                    MethodParameter methodParameter, Type type,
                                    Class<? extends HttpMessageConverter<?>> aClass) {
            return o;
        }  
    }
    

     自定义 内容处理类

    public class CustomerHttpInputMessage implements HttpInputMessage {
            private HttpHeaders headers;
    
            private InputStream body;
            public CustomerHttpInputMessage(HttpInputMessage inputMessage) {
                this.headers = inputMessage.getHeaders();
                try {
                    //获取内容
                    String reqBody = IOUtils.toString(inputMessage.getBody(),
                            ENCODING);
    
                    //解析string为JSON
                    JSONObject jsonObject = JSONObject.parseObject(reqBody);
                    String reqData = jsonObject.getString("securityData");
                    //参数的解密处理
                    //.................
    
                    this.body = IOUtils.toInputStream(reqData, ENCODING);
                } catch (Exception ex) {
                }
            }
    
            @Override
            public InputStream getBody() throws IOException {
                return body;
            }
    
            @Override
            public HttpHeaders getHeaders() {
                return headers;
            }
        }

    调用接口时对参数进行约定方式的加密处理。使用 application/json 等文本请求。

    修改方法入参

    很多操作可能需要记录,修改人、更新人。这样会导致重复代码比较多,那么我们可以在此处做增强处理。 当然我们也可以通过AOP 实现

    /**
    *
    * 此方法返回结果就是 方法的入参  
    */
    public Object afterBodyRead(Object o, HttpInputMessage httpInputMessage,
                                    MethodParameter methodParameter, Type type,
                                    Class<? extends HttpMessageConverter<?>> aClass){
    
    		//获取当前的登录人
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.currentRequestAttributes();
            HttpServletRequest httpServletRequest = servletRequestAttributes.getRequest();
            User user = (User) httpServletRequest.getSession().getAttribute("userInfo");
    
    
    		 // 参数类型是 Map,则使用put 方法添加
            if(o instanceof Map){
                Map targetMap = (Map) o;
                targetMap.put("createUser",user.getName());
            }else{
                //其他类型
                // type 通常是指方法的入参 参数类型
                Class cls = (Class) type;
                try {
                    Method method = cls.getMethod("setName",String.class);
                    method.setAccessible(true);
                    method.invoke(o,user.getName());
                } catch (Exception  e) {
                    e.printStackTrace();
                }
            }
    }
    

     @RequestParam

    从Request请求中获取指定的参数
    可以设置的属性

    required : 默认为itrue 参数必须存在。参数不存在时抛出异常
    defaultValue :设置参数默认值。 只有在required 设置为false的情况下,才会生效 。
    name | value 关联的参数名称。 request.getParameter(name);

    有点乱~大家将就着看吧~嘿嘿

  • 相关阅读:
    11 2
    10 29
    10 22
    dp的本质
    笛卡尔树小结
    Gitlab 备份迁移恢复报错gtar: .: Cannot mkdir: No such file or directory
    升级Jenkins版本
    当linux中的所有指令突然不能使用的时候
    合并范围
    每股收益列报计算
  • 原文地址:https://www.cnblogs.com/47Gamer/p/13355956.html
Copyright © 2020-2023  润新知