【java知识点】常用注解解析

1.@Order

在这里插入图片描述
在这里插入图片描述

@Order注解可应用于类、方法和字段属性上;
@Order注解定义了Spring IOC容器中Bean的执行顺序的优先级,而不是定义Bean的加载顺序,Bean的加载顺序不受@Order或Ordered接口的影响(个人理解:在项目启动的时候会将所有的bean进行注入,注入后我们调用所需要的bean的先后顺序不会受到这@Order注解的影响)。

简单的应用:

package com.example.practice.service.impl;

import com.example.practice.service.IUserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements IUserService {


    @Override
    public void getUserInfo() {
        System.out.println("成功获取用户信息");
    }
}
package com.example.practice.service.impl;

import com.example.practice.service.ICarService;
import org.springframework.stereotype.Service;

@Service
public class CarServiceImpl implements ICarService {


    @Override
    public void getCarInfo() {
        System.out.println("成功获取车辆信息");
    }
}

package com.example.practice.anno;

import com.example.practice.service.impl.UserServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

@Configuration
@Order(1)
public class AConfig {

    @Bean
    public UserServiceImpl aUserService() {
        System.out.println("UserServiceImpl加载完成");
        return new UserServiceImpl();
    }

}
package com.example.practice.anno;

import com.example.practice.service.impl.CarServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;

@Configuration
@Order(2)
public class BConfig {

    @Bean
    public CarServiceImpl aCarService() {
        System.out.println("CarServiceImpl加载完成");
        return new CarServiceImpl();
    }

}

测试方法:

package com.example.practice.MainTest;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext("com.example.practice.anno");
    }

}

运行结果为:
在这里插入图片描述
可见,@Order注解中的数字越小,优先级越高。

2.ImportBeanDefinitionRegistrar接口的调用

在实际业务中,当我们需要注入某个bean的时候可以采用@Component、@Service等注解进行注入或者通过xml文件进行注入。但是存在一些情况,在不使用Spring注解的情况下,也可以进行注入,此时就需要利用Spring提供的直接注入的方法 -》 实现ImportBeanDefinitionRegistrar接口

ImportBeanDefinitionRegistrar基本介绍:
1>ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载,通常是启动类或配置类。
2>使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成bean。
3>实现该接口的类拥有注册bean的能力。

package com.example.practice.conf;

import org.springframework.context.annotation.Import;

import java.lang.annotation.*;

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Import({MyImportBeanDefinitionRegistrar.class})
public @interface ImportBean {

    /**
     * 要注入的class集合
     */
    Class<?>[] value() default {};

}

package com.example.practice.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {

    private String userName;

    private Integer age;

    @Override
    public String toString() {
        return "User{" +
                "userName='" + userName + '\'' +
                ", age=" + age +
                '}';
    }
}

package com.example.practice.conf;

import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {


    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        // 获取注解ImportBean的属性
        AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(ImportBean.class.getName()));
        Class<?>[] classValues = annotationAttributes.getClassArray("value");
        // 注入BeanDefinition
        for (Class classValue : classValues) {
            RootBeanDefinition beanDefinition = new RootBeanDefinition(classValue);
            MutablePropertyValues propertyValues = beanDefinition.getPropertyValues();
            propertyValues.add("userName","god");
            propertyValues.add("age",18);
            // 注册bean
            registry.registerBeanDefinition("user",beanDefinition);
        }

    }
}

package com.example.practice;

import com.example.practice.conf.ImportBean;
import com.example.practice.domain.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude= DataSourceAutoConfiguration.class)
@ImportBean({User.class})
public class PracticeApplication {

    public static void main(String[] args) {
        SpringApplication.run(PracticeApplication.class, args);
    }

}

package com.example.practice.controller;

import com.example.practice.domain.User;
import lombok.Getter;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/test")
@Getter
public class UserController {
    

    @Resource
    private User user;

    @PostMapping(value = "/getUser")
    public void getUser() {
        System.out.println(user.toString());
    }

}

运行结果为:
在这里插入图片描述
采用注解的方式,可以随时确定哪些bean需要进行注入。

3.HandlerInterceptor接口的调用

拦截器是在面向切面编程中应用的,就是在你的service或者一个方法前调用一个方法,或者在方法后调用一个方法。是基于JAVA的反射机制。

1.拦截器(Interceptor)执行顺序:
1>请求到达 DispatcherServlet
2>DispatcherServlet 发送至 Interceptor ,执行 preHandle
3>请求达到 Controller
4> 请求结束后,postHandle 执行

2.拦截器(Interceptor)和过滤器(Filter)的执行顺序
过滤前->拦截前->Action处理->拦截后->过滤后

3.使用方法
1>预处理preHandle()方法
用户发送请求时,先执行preHandle()方法。会先按照顺序执行所有拦截器的preHandle方法,一直遇到return false为止,比如第二个preHandle方法是return false,则第三个以及以后所有拦截器都不会执行。若都是return true,则执行用户请求的url方法。

2>后处理postHandle()方法
调用了Service并返回ModelAndView,但未进行页面渲染,可以在这里继续修改ModelAndView

3>返回处理afterCompletion()方法
已经渲染了页面,在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。

注:一般使用preHandle这个拦截器进行预处理,对url进行请求拦截

自定义拦截器:

package com.example.practice.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;


@Component
public class TestInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle方法执行");
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle方法执行");
        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion方法执行");
        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
    }
}

注册拦截器:

package com.example.practice.interceptor;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class TestInterceptorAdapterConfig implements WebMvcConfigurer {

    @Autowired
    private TestInterceptor testInterceptor;


    public TestInterceptorAdapterConfig() {}

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.testInterceptor).excludePathPatterns(new String[]{"*.html"});
    }
}

执行结果如下:
在这里插入图片描述

4.ImportSelector接口的调用

在@Configuration标注的Class上可以使用@Import引入其它的配置类,其实它还可以引入org.springframework.context.annotation.ImportSelector实现类。ImportSelector接口只定义了一个selectImports(),用于指定需要注册为bean的Class名称。当在@Configuration标注的Class上使用@Import引入了一个ImportSelector实现类后,会把实现类中返回的Class名称都定义为bean。

自定义实体类:

package com.example.practice.domain;


public class Depart {

    private Integer depId;

    private String depName;

    public Integer getDepId() {
        return depId;
    }

    public void setDepId(Integer depId) {
        this.depId = depId;
    }

    public String getDepName() {
        return depName;
    }

    public void setDepName(String depName) {
        this.depName = depName;
    }
}

定义Configuration类:

package com.example.practice.importBean;

import org.springframework.context.annotation.Configuration;

@Configuration
public class TestImportSelectorConfiguration {
}

package com.example.practice.controller;

import com.example.practice.AOP.TestAnno;
import com.example.practice.domain.Depart;
import com.example.practice.domain.User;
import com.example.practice.service.IUserService;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/test")
@Getter
public class UserController {

    @Resource
    private Depart depart;

    @PostMapping(value = "/getDepart")
    public void getDepart() {
        System.out.println(depart.toString());
    }

}

项目启动报错:
在这里插入图片描述
解决方案:
1.通过@Import进行Depart类的引入:

package com.example.practice.importBean;

import com.example.practice.domain.Depart;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(Depart.class)
public class TestImportSelectorConfiguration {
}

运行结果为:
在这里插入图片描述
2.通过ImportSelector进行引入:

package com.example.practice.importBean;

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

public class TestImportSelector implements ImportSelector {

    public TestImportSelector() {}

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{"com.example.practice.domain.Depart"};
    }
}

package com.example.practice.importBean;

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(TestImportSelector.class)
public class TestImportSelectorConfiguration {
}

运行结果为:
在这里插入图片描述

上面的例子是有关实体bean的,那接口的呢?

1.接口的定义

public interface HelloService {
    void sayHello();
}

public class HelloServiceA implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("Hello A");
    }
}

public class HelloServiceB implements HelloService {
    @Override
    public void sayHello() {
        System.out.println("Hello B");
    }
}

2.selector的定义

public class HelloImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
//        return new String[]{HelloServiceA.class.getName(), HelloServiceB.class.getName()};
        //1.获取扫描的包路径
        String[] basePackages = null;
        if (importingClassMetadata.hasAnnotation(ComponentScan.class.getName())) {
            Map<String, Object> annotationAttributes = importingClassMetadata.getAnnotationAttributes(ComponentScan.class.getName());
            basePackages = (String[]) annotationAttributes.get("basePackages");
        }
        // 默认扫描当前路径
        // ComponentScan的basePackages默认为空数组
        if (basePackages == null || basePackages.length == 0) {
            String basePackage = null;
            try {
                basePackage = Class.forName(importingClassMetadata.getClassName()).getPackage().getName();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
            basePackages = new String[] {basePackage};
        }
        ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false);
        //3.需要的是接口HelloService的实现类
        TypeFilter helloServiceFilter = new AssignableTypeFilter(HelloService.class);
        scanner.addIncludeFilter(helloServiceFilter);
        Set<String> classes = new HashSet<>();
        //4.扫描包路径下接口HelloService的实现类
        for (String basePackage : basePackages) {
            scanner.findCandidateComponents(basePackage).forEach(beanDefinition -> classes.add(beanDefinition.getBeanClassName()));
        }
        return classes.toArray(new String[classes.size()]);
    }

    @Override
    public Predicate<String> getExclusionFilter() {
        return ImportSelector.super.getExclusionFilter();
    }
}

3.configuration的定义

@Configuration
@ComponentScan("com.example.springbootpractice.service")
@Import(HelloImportSelector.class)
public class HelloConfiguration {
}

4.测试类

@SpringBootTest
@ContextConfiguration(classes= HelloConfiguration.class)
public class HelloImportSelectorTest {

    @Autowired
    private List<HelloService> helloServices;

    @Test
    public void test() {
        this.helloServices.forEach(HelloService::sayHello);
    }

}

在这里插入图片描述
ImportSelector真正的作用到底是什么呢?
其实Spring公司既然这么设计,那肯定是有用的。那么有什么用呢?设想这样一个场景,如果有些功能我们并不需要Spring在一开始就加载进去,而是需要Spring帮助我们把这些功能动态加载进去,这时候这个ImportSelector的作用就来了。我们完全可以把实现这个接口的类做成一个开关,用来开启或者关闭某一个或者某些功能类。

@Retention(RetentionPolicy.RUNTIME) //开启运行时加载
@Import(HelloImportSelector.class)
public @interface EnableHelloSelector {
}

```java
@Configuration
@ComponentScan("com.example.springbootpractice.service")
@EnableHelloSelector
public class HelloConfiguration {
}

DeferredImportSelector接口是ImportSelector子类,直接翻译就是延时加载ImportSelector,用法和ImportSelector功能基本一样。

public class HelloDeferredImportSelector implements DeferredImportSelector {

    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        return new String[]{HelloServiceA.class.getName()};
    }

    @Override
    public Predicate<String> getExclusionFilter() {
        return null;
    }



    @Override
    public Class<? extends Group> getImportGroup() {
        // 这个返回值决定调用DeferredImportSelector.selectImports  如果null则还是调用Group.selectImports
        return  MyGroup.class;
    }

    // 如果getImportGroup返回自定义Group , 会调用自定义Group的process方法
    // 如果getImportGroup返回 null,会调用DefaultDeferredImportSelectorGroup的process方法,即调用selectImports
    private static class MyGroup
            implements DeferredImportSelector.Group{

        AnnotationMetadata metadata;
        @Override
        public void process(AnnotationMetadata metadata, DeferredImportSelector selector) {
            this.metadata=metadata;
        }

        @Override
        public Iterable<Entry> selectImports() {
            List<Entry> list=new ArrayList<>();
            list.add(new Entry(this.metadata,HelloServiceB.class.getName()));
            return  list;
        }
    }

}

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值