zoukankan      html  css  js  c++  java
  • Spring 注解学习 使用示例

    学习Sping注解,编写示例,最终整理成文章。如有错误,请指出。

    该文章主要是针对新手的简单使用示例,讲述如何使用该注释,没有过多的原理解析。

    已整理的注解请看右侧目录。写的示例代码也会在结尾附出。

    配置类相关注解

    @Configuration

    声明当前类为配置类,把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。该类等价 与XML中配置beans。

    @Configuration
    public class MyConfig {
    }

    @Bean

    注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()的方法和destroy()方法,然后在@Bean(initMethod="init",destroyMethod="destroy")定义,在构造之后执行init,在销毁之前执行destory。

    @Configuration
    @ComponentScan("com.blackcat.annotation.bean")
    public class MyConfig {

    /**
    * <p> 描述 : @Bean的形式是使用的话, bean的默认名称是方法名
    * @author : blackcat
    * @date : 2020/5/23 16:20
    * @see App ctx.getBean("user") 得到bean
    */
    @Bean(initMethod="init",destroyMethod="destroy")
    public User user(){
    return new User();
    }
    }
    /**
     * <p> 描述 :Bean
     * @author : blackcat
     * @date : 2020/5/23 16:06
     * @see MyConfig  需要看MyConfig
     *
     */
    @Data
    public class User {
        private String name="zhang";
    
        public void init(){
            System.out.println("init");
        }
    
        public void destroy(){
            System.out.println("destroy");
        }
    }

    @ComponentScan

    注解用于启用组件扫描,其作用同xml中配置<context:component-scan>。若不配置其value值,它会以配置类所在的包作为基础包(base package)来扫描组件。

    参数说明:

    basePackages:扫描的路径
    excludeFilters:排除 过滤条件
    includeFilters:包含 过滤条件
    useDefaultFilters:若使用包含的用法,需要把useDefaultFilters属性设置为false(true表示扫描全部的)

    FilterType的类型:

    FilterType.ANNOTATION: @Controller @Service @Repository @Compent
    FilterType.ASSIGNABLE_TYPE:指定组件
    FilterType.CUSTOM: 自定义的
    FilterType.REGEX: 正则表达式的(不常用,没写示例)
    FilterType.ASPECTJ: aspectj类型的(不常用,没写示例)

    最简单示例

    自动扫描指定包com.blackcat.annotation.compentscan下所有使用@Service,@Component,@Controller,@Repository的类并注册。

    @ComponentScan(basePackages = {"com.blackcat.annotation.compentscan"})
    @Configuration
    public class MyConfig {
    
    }

    排除excludeFilters

    将不会加载所有Controller类 及UserService类(指定类)。

    @ComponentScan(basePackages = {"com.blackcat.annotation.componentscan"},excludeFilters = {
        @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),
        @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {UserService.class})
    })
    @Configuration
    public class MyConfig {
    
    }

    包含includeFilters

    加载除所有使用@Service,@Component,@Controller,@Repository外,以及包含过滤条件的MyFilterType,需要把useDefaultFilters属性设置为false(true表示扫描全部的)。

    @ComponentScan(basePackages = {"com.blackcat.annotation.componentscan"},includeFilters = {
        @ComponentScan.Filter(type = FilterType.CUSTOM,value = MyFilterType.class)
    },useDefaultFilters = false)
    @Configuration
    public class MyConfig {
    
    }
    /**
     * <p> 描述 :FilterType.CUSTOM 自定义类型使用
     * @author : blackcat
     * @date : 2020/5/23 16:40
     */
    public class MyFilterType implements TypeFilter {
    
        @Override
        public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
            //获取当前类的注解源信息
            AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
    
            //获取当前类的class的源信息
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            //获取当前类的资源信息
            Resource resource =  metadataReader.getResource();
            System.out.println("类的路径:"+classMetadata.getClassName());
            if(classMetadata.getClassName().contains("dao")) {
                return true;
            }
            return false;
        }
    }

    组合使用

    将excludeFilters与includeFilters组合使用。

    @ComponentScan(basePackages = {"com.blackcat.annotation.componentscan"},
        excludeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class})},
        includeFilters = {
            @ComponentScan.Filter(type = FilterType.ANNOTATION,value = Repository.class)
    })
    @Configuration
    public class MyConfig {
    
    }

    @Bean的属性支持

    @Scope

    设置Spring容器如何新建Bean实例(方法上,得有@Bean) 作用域 。在不指定@Scope的情况下,所有的bean都是单实例的bean,而且是饿汉加载(容器启动实例就创建好了)。

    其设置类型包括:Singleton,Protetype,Request(无代码示例) ,Session (无代码示例) ,GlobalSession(无代码示例) 

        @Bean
        @Scope(value = "singleton")
        public User user4(){
            System.out.println("容器开始创建bean.........");
            return new User();
        }

    Singleton

    单实例的(默认) 全局有且仅有一个实例。

    Protetype

    表示为多实例的,每次获取Bean的时候会有一个新的实例,而且还是懒汉模式加载(IOC容器启动的时候,并不会创建对象,而是 在第一次使用的时候才会创建)。

    每次连接请求,都会生成一个bean实例,也会导致一个问题,当请求数越多,性能会降低,因为创建的实例,导致GC频繁,gc时长增加。

    request

    同一次请求 ,表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP request内有效  (web阶段时使用,无示例)。

    session

    同一个会话级别,表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTP session内有效  (web阶段时使用,无示例)。

    globalsession

    全局session中的一般不常用。给每一个 global http session新建一个Bean实例  类似于标准的HTTP Session作用域,不过它仅仅在基于portlet的web应用中才有意义 。

    @PostConstruct

    由JSR-250提供,在构造函数执行完之后执行,等价于xml配置文件中bean的initMethod,用于指定初始化方法  标注在方法上方,该方法在构造函数执行完成之后执行。

    被@PostConstruct修饰的方法会在服务器加载Servle的时候运行,并且只会被服务器执行一次。

    服务器加载:Servlet -> servlet 构造函数的加载 -> postConstruct ->init(init是在service 中的初始化方法. 创建service 时发生的事件.) ->Service->destory->predestroy->服务器卸载serlvet。

    如果想在生成对象时候完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么就无法在构造函数中实现。为此,可以使用@PostConstruct注解一个方法来完成初始化,@PostConstruct注解的方法将会在依赖注入完成后被自动调用。

    public class User {
        private String name="zhang";
    
        public void init(){
            System.out.println("init");
        }
    
        public void destroy(){
            System.out.println("destroy");
        }
    
        @PostConstruct
        public void PostConstruct(){
            System.out.println("@PostConstruct将在依赖注入完成后被自动调用");
        }
    }

    @PreDestroy

    由JSR-250提供,在Bean销毁之前执行,等价于xml配置文件中bean的destroyMethod,用于指定销毁方法(用在方法上)  摧毁注解 默认 单例  启动就加载 。

    服务器加载:Servlet -> servlet 构造函数的加载 -> postConstruct ->init(init是在service 中的初始化方法. 创建service 时发生的事件.) ->Service->destory->predestroy->服务器卸载serlvet。

    根据上个注释代码,就不过多粘贴。

    @PreDestroy
    public void PreDestroy(){
        System.out.println("XXX 正在被容器删除");
    }

    @Lazy

    用于标识bean是否需要延迟加载。主要针对单实例的bean 容器启动的时候,不创建对象,在第一次使用的时候才会创建该对象。

        @Bean
        @Lazy
        public User user4(){
            System.out.println("容器开始创建bean.........");
            return new User();
        }

    @Primary

    自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者。

    该示例请看:com.blackcat.annotation.autowired包下的代码。

    
    
    @Configuration
    @ComponentScan(basePackages = "com.blackcat.annotation.autowired")
    public class MyConfig {

    @Primary
    @Bean
    public UserDao userDao2() {
    UserDao userDao = new UserDao();
    userDao.setFlag(2);
    return userDao;
    }

    @Bean
    public UserDao userDao() {
    UserDao userDao = new UserDao();
    userDao.setFlag(1);
    return userDao;
    }
    }
    @Service
    public class BaiDuService {

    @Autowired
    private UserDao userDao;

    @Override
    public String toString() {
    return "BaiDuService{" +
    "userDao=" + userDao +
    '}';
    }

    }
    public class UserDao {
    
        private int flag=1;
    
        @Override
        public String toString() {
            return "UserDao{" +
                    "flag=" + flag +
                    '}';
        }
    }

    @DependsOn

    定义Bean初始化及销毁时的顺序。有很多场景需要bean B应该被先于bean A被初始化,从而避免各种负面影响。我们可以在bean A上使用@DependsOn注解,告诉容器bean B应该先被初始化。

    例如:bean A 间接依赖 bean B。如Bean B应该需要更新一些全局缓存,可能通过单例模式实现且没有在spring容器注册,bean A需要使用该缓存;因此,如果bean B没有准备好,bean A无法访问。

    示例通过事件机制说明,发布者和监听者,然后通过spring配置运行。

    /**
     * <p> 描述 : 事件管理类,维护监听器列表,通过单例方法获取事件管理器,可以增加监听器或发布事件。
     * @author : blackcat
     * @date  : 2020/7/31 10:03
    */
    public class EventManager {
    
        private final List<Consumer<String>> listeners = new ArrayList<>();
    
        private EventManager() {
        }
    
        private static class SingletonHolder {
            private static final EventManager INSTANCE = new EventManager();
        }
    
        public static EventManager getInstance() {
            return SingletonHolder.INSTANCE;
        }
    
        public void publish(final String message) {
            listeners.forEach(l -> l.accept(message));
        }
    
        public void addListener(Consumer<String> eventConsumer) {
            listeners.add(eventConsumer);
        }
    }
    /**
     * <p> 描述 : 事件监听者,可以增加监听器。
     * @author : blackcat
     * @date  : 2020/7/31 10:04
    */
    public class EventListenerBean {
    
        private void initialize() {
            EventManager.getInstance().
                    addListener(s ->
                            System.out.println("事件监听者 : " + s));
        }
    }
    /**
     * <p> 描述 : 事件发布类,通过EventManager类发布事件。
     * @author : blackcat
     * @date  : 2020/7/31 10:04
    */
    public class EventPublisherBean {
    
        public void initialize() {
            System.out.println("事件发布类 initializing");
            EventManager.getInstance().publish("event published from EventPublisherBean");
        }
    }

    总结

    如果我们注释掉@DependsOn("eventListener"),我们可能不确定获得相同结果。尝试多次运行main方法,偶尔我们将看到EventListenerBean 没有收到事件。为什么是偶尔呢?因为容器启动过程中,spring按任意顺序加载bean。

    那么当不使用@DependsOn可以让其100%确定吗?可以使用@Lazy注解放在eventListenerBean ()上。因为EventListenerBean在启动阶段不加载,当其他bean需要其时才加载。这次我们仅EventListenerBean被初始化。

    EventPublisherBean initializing

    现在从新增加@DependsOn,也不删除@Lazy注解,输出结果和第一次一致,虽然我们使用了@Lazy注解,eventListenerBean在启动时仍然被加载,因为@DependsOn表明需要EventListenerBean。 

    该示例参考文章:https://blog.csdn.net/neweastsun/article/details/78775371

    声明bean的注解

    @Component

    泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。可通过@Component("XX")声明bean的名字,默认名称是类名头字母小写。

    @Component
    public class MyLog {
        private String name="123456";
    }
    @Component("info")
    public class MyInfo {
    private String name="123456";
    }
    @Configuration
    @ComponentScan(basePackages = "com.blackcat.annotation.component")
    public class MyConfig {

    }
    public static void main(String[] args) {
            // 容器中读取Bean
            AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MyConfig.class);
            System.out.println(ctx.getBean(MyLog.class));
            System.out.println(ctx.getBean("info"));
        }

    @Service

    在业务逻辑层使用(service层),对应的是业务层Bean。用于标注业务层组件。可通过@Service("XX")声明bean的名字,默认名称是类名头字母小写。

    public interface UserService {
    .....
    }
    @Service
    public class UserServiceImpl implements UserService {
    .....
    }
    // 注入userService
    @Resourceprivate UserService userService;

    @Repository

    在数据访问层使用(dao层),对应数据访问层Bean,用于标注数据访问组件,即DAO组件。可通过@Repository("XX")声明bean的名字,默认名称是类名头字母小写。

    public interface BaseDao<T> {
    }
    @Repository("userDao")
    public class UserDaoImpl implements BaseDao<User> {
    }
    // 注入userDao
    @Resource(name = "userDao")
    private BaseDao<User> userDao;

    @Controller

    在展现层使用,控制器的声明。@Controller对应表现层的Bean,也就是Action。

    @Controller
    public class UserController {
    }

    注入bean的注解

    @Autowired

    由Spring提供。默认按类型装配。

    自动装配首先时按照类型进行装配,若在IOC容器中发现了多个相同类型的组件,那么就按照 属性名称来进行装配。
    例如:现在容器中有两个userDao类型的组件,一个叫userDao 一个叫userDao2,我们通过@AutoWired 来修饰的属性名称时userDao,就加载容器的userDao组件,若属性名称为 userDao2 那么他就加载的时userDao2组件。

    public class UserDao {
        private int flag=1;
        @Override
        public String toString() {
            return "BaseDao{" +
                    "flag=" + flag +
                    '}';
        }
    }
    @Configuration
    @ComponentScan(basePackages = "com.blackcat.annotation.autowired")
    public class MyConfig {
        @Bean
        public UserDao userDao2() {
            UserDao userDao = new UserDao();
            userDao.setFlag(2);
            return userDao;
        }
    
        @Bean
        public UserDao userDao() {
            UserDao userDao = new UserDao();
            userDao.setFlag(1);
            return userDao;
        }
    
        @Bean(autowire = Autowire.BY_NAME)
        public UserService userService() {
            return new UserService();
        }
    }
    public class UserService {
        private UserDao userDao;
        @Override
        public String toString() {
            return "UserService{" +
                    "userDao=" + userDao +
                    '}';
        }
    }

     

    如果我们想使用按名称装配,可以结合@Qualifier注解一起使用, @Autowired @Qualifier("XX") 存在多个实例配合使用。

    @Service
    public class BaiDuService {
    
        @Qualifier("userDao")
        @Autowired
        private UserDao userDao2;
    
        @Override
        public String toString() {
            return "BaiDuService{" +
                    "userDao=" + userDao2 +
                    '}';
        }
    
    }

    @Autowired(required = false) 的意思是装配的上就装,装不上就不装。

    示例:@Qualifier("userDao3") 容器中即没有userDao3也没有userDao2,那么在装配的时候就会抛出异常。若我们想不抛异常 ,我们需要指定 required为false的时候可以了。

     

     

    @Autowired 可标注在set方法上或构造方法上。

    public class MyAspect {
    
        private MyLog myLog;
    
        @Override
        public String toString() {
            return "MyAspect{" +
                    "myLog=" + myLog +
                    '}';
        }
    
        /**
         * <p> 描述 : 标注在set方法上
         * @author : blackcat
         * @date  : 2020/5/26 14:56
         */
        //@Autowired
        public void setMyLog(MyLog myLog) {
            this.myLog = myLog;
        }
    
        /**
         * <p> 描述 : 标注在构造方法上
         * @author : blackcat
         * @date  : 2020/5/26 14:56
        */
        @Autowired
        public MyAspect(MyLog myLog) {
            this.myLog = myLog;
        }
    } 
    @Component
    @ToString
    public class MyLog {
    
    }

    @Autowired 也可标注在配置类上的入参中(可以不写@Autowired)。

        @Bean
        public MyAspect myAspect(@Autowired MyLog myLog) {
            MyAspect myAspect = new MyAspect(myLog);
            return myAspect;
        }

    @Inject

    由JSR-330提供。需要导入jar包依赖。

            <!--JSR330规范-->
            <dependency>
                <groupId>javax.inject</groupId>
                <artifactId>javax.inject</artifactId>
                <version>1</version>
            </dependency>    
    @Service
    public class UserService {
    
        /**
         * 需要导入jar包依赖
         * 支持@Primary功能 ,但是没有Require=false的功能
         */
        @Inject
        private UserDao userDao;
    
        @Override
        public String toString() {
            return "UserService{" +
                    "userDao=" + userDao +
                    '}';
        }
    }

    @Resource

    由JSR-250提供。默认按名称装配,当找不到与名称匹配的bean才会按类型装配。

    @Service
    public class UserService {
    
        /**
         * 功能和@AutoWired的功能差不多一样,但是不支持@Primary 和 @Qualifier的支持
         */
        @Resource
        private UserDao userDao;
    
        @Override
        public String toString() {
            return "UserService{" +
                    "userDao=" + userDao +
                    '}';
        }
    }

    @Value注解

    /**
     * <p> 描述 : Value注解
     * @author : blackcat
     * @date  : 2020/5/25 17:45
    */
    @Data
    @Component
    public class User {
    
        /** 注入普通字符 */
        @Value("cat")
        private String userName;
    
        /** spel方式来赋值 */
        @Value("#{28-3}")
        private Integer age;

    /** * 注入操作系统属性 * 需要有系统配置文件类SystemProperties */ @Value("#{systemProperties['os.name']}") private String osName; /** 注入表达式结果 */ @Value("#{ T(java.lang.Math).random() * 100 }") private String randomNumber; /** * 注入配置文件 * 1.编写配置文件 test.properties * 2.加载配置文件类@PropertySource */ @Value("${book.name}") private String book; /** 注入网站资源 */ @Value("http://www.baudu.com") private Resource url; }
    /**
     * <p> 描述 :
     * @author : blackcat
     * @date  : 2020/5/25 17:45
    */
    @Configuration
    @PropertySource(value = {"classpath:test.properties"})// 指定外部文件的位置
    public class MyConfig {
    
        @Bean
        public User user() {
            return new User();
        }
    }

    test.properties

    book.name=电子书

    条件注解

    以@ConditionalOnXX 为SpringBoot注解。了解完Conditional注解的原理之后,就方便了解ConditionalOnXX注解的原理了。

    @Conditional

    它的作用是按照一定的条件进行判断,满足条件给容器注册bean。

    @Conditional的定义:

    //此注解可以标注在类和方法上
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME) 
    @Documented
    public @interface Conditional {
        Class<? extends Condition>[] value();
    }

    从代码中可以看到,需要传入一个Class数组,并且需要继承Condition接口。

    示例:现在有两个组件MyAspect,MyLog。现在有个条件判断,当容器内没有MyAspect就不注入MyLog。

    public class MyAspect {
        public MyAspect() {
            System.out.println("MyAspect组件");
        }
    }
    public class MyLog {
        public MyLog() {
            System.out.println("MyLog组件");
        }
    }

    条件判断类MyCondition,需要实现Condition接口,并重写方法来自定义match规则。

    /**
     * <p> 描述 :自定义条件判断
     *
     * @author : blackcat
     * @date : 2020/5/24 14:05
     */
    public class MyCondition  implements Condition {
    
        @Override
        public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
            //判断容器中是否有MyAspect的组件
            if(conditionContext.getBeanFactory().containsBean("myAspect")) {
                return true;
            }
            return false;
        }
    }

    从下面的运行结果可以看出,当MyAspect注入成功时,MyLog注入成功。而MyAspect没注入成功,MyLog也没有注入成功。

    条件的判断是根据MyCondition类中判断,判断MyAspect是否存在。

    标注在方法上:

    一个方法只能注入一个bean实例,所以@Conditional标注在方法上只能控制一个bean实例是否注入。

    标注在类上:

    一个类中可以注入很多实例,@Conditional标注在类上就决定了一批bean是否注入。

    多个条件类:

    前言中说,@Conditional注解传入的是一个Class数组,存在多种条件类的情况。

    第一个条件类实现的方法返回true,第二个返回false,则结果false,不注入进容器。

    第一个条件类实现的方法返回true,第二个返回true,则结果true,注入进容器中。

    @ConditionalOnBean

    当给定的在bean存在时,则实例化当前Bean。

    @Data
    public class City {
        /** 城市名称 */
        private String cityName;
        /** 城市code */
        private Integer cityCode;
    }
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class People {
        /** 姓名 */
        private String name;
        /** 年龄 */
        private Integer age;
        /** 城市信息 */
        private City city;
    }

    @ConditionalOnMissingBean

    当给定的在bean不存在时,则实例化当前Bean。

    @ConditionalOnClass

    当给定的类名在类路径上存在,则实例化当前Bean。原理同@ConditionalOnBean差不多。City2并不存在,所以People没有注入成功。

    @ConditionalOnMissingClass

    当给定的类名在类路径上不存在,则实例化当前Bean。结果同ConditionalOnClass相反。City2并不存在,所以People注入成功。

    @ConditionalOnExpression

    基于SpEL表达式作为判断条件。当括号中的内容为true时,使用该注解的类被实例化。

    @ConditionalonalOnJava

    基于JVM版本作为判断条件。只有运行指定版本的 Java 才会加载 Bean。

        /**
         * 只有运行指定版本的 Java 才会加载 Bean
         */
        @ConditionalOnJava(JavaVersion.EIGHT)
        @Bean
        public People people() {
            return new People();
        }

    @ConditionalOnJndi

    在JNDI存在的条件下查找指定的位置。只有指定的资源通过 JNDI 加载后才加载 bean。JNDI(Java Naming and Directory Interface,Java命名和目录接口)。

        @ConditionalOnJndi("java:comp/env/foo")
        @Bean
        public People people() {
            return new People();
        }

    @ConditionalOnWebApplication

    当前项目是web项目的情况下。只有运行在 web 应用里才会加载这个 bean。

        @ConditionalOnWebApplication
        @Bean
        public People people() {
            return new People();
        }

    @ConditionalOnNotWebApplication

    当前项目不是web项目的条件下。与@ConditionalOnWebApplication相反,在非 web 环境才加载 bean。

        @ConditionalOnNotWebApplication
        @Bean
        public People people() {
            return new People();
        }

    @ConditionalOnResource

    类路径是否有指定的值。如果我们要加载的 bean 依赖指定资源是否存在于 classpath 中,那么我们就可以使用这个注解。

    @ConditionalOnSingleCandidate

    当指定Bean在容器中只有一个,后者虽然有多个但是指定首选的Bean。

    只有指定类已存在于 BeanFactory 中,并且可以确定单个候选项才会匹配成功 BeanFactory 存在多个 bean 实例,但是有一个 primary 候选项被指定(通常在类上使用 @Primary 注解),也会匹配成功。

    没有写示例,额.....因为我还不会。有会的人可以告诉一下。我学习一下后面再补上。

    方法原注释:

    @Conditional,仅当指定类的bean已包含在BeanFactory中并且可以确定单个候选时匹配。如果BeanFactory中已经包含多个匹配的bean实例,但是已经定义了一个主候选实例,那么这个条件也将匹配;实际上,如果一个bean与定义的类型自动连接成功,那么条件匹配。
    
    该条件只能匹配到目前为止由应用程序上下文处理的bean定义,因此,强烈建议仅在自动配置类上使用此条件。如果候选bean可能由另一个自动配置创建,请确保使用此条件的bean在之后运行。

    异步相关

    @Async

    在实际执行的bean方法使用该注解来申明其是一个异步任务(方法上或类上所有的方法都将异步,需要@EnableAsync开启异步任务)。

    @Async 必须不同类间调用: A类--》B类.C方法()(@Async注释在B类/方法中),如果在同一个类中调用,会变同步执行,例如:A类.B()-->A类.@Async C(),

    原因是:底层实现是代理对注解扫描实现的,B方法上没有注解,没有生成相应的代理类。

    @Configuration
    @EnableAsync
    public class ThreadPoolConfig {
    
        private static int corePoolSize=30;
    
        private static int maxPoolSize=100;
    
        private static int queueCapacity=100;
    
        private static int keepAliveSeconds=300;
    
        @Bean
        public TaskExecutor jobExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            // 设置核心线程数
            executor.setCorePoolSize(corePoolSize);
            // 设置最大线程数
            executor.setMaxPoolSize(maxPoolSize);
            // 设置队列容量
            executor.setQueueCapacity(queueCapacity);
            // 设置线程活跃时间(秒)
            executor.setKeepAliveSeconds(keepAliveSeconds);
            // 设置默认线程名称
            executor.setThreadNamePrefix("async-job-thread-");
            // 设置拒绝策略rejection-policy:当pool已经达到max size的时候,如何处理新任务 CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            // 等待所有任务结束后再关闭线程池
            executor.setWaitForTasksToCompleteOnShutdown(true);
            return executor;
        }
    
        @Bean
        public AsyncBean asyncBean(){
            return new AsyncBean();
        }
    
    }
    public class AsyncBean {
    
        /**
         * Async 必须不同类间调用: A类--》B类.C方法()(@Async注释在B类/方法中),
         * 如果在同一个类中调用,会变同步执行,例如:A类.B()-->A类.@Async C()
         *
         * 如果在同一个类中调用,会变同步执行
        */
        @Async("jobExecutor")
        public void asyncMethod() throws InterruptedException {
            System.out.println(Thread.currentThread().getName()+"异步执行");
            Thread.sleep(1000);
        }
    
        public static void main(String[] args) throws InterruptedException {
            /** 如果在同一个类中调用,会变同步执行 */
            System.out.println(Thread.currentThread().getName()+"主线程请求异步执行asyncMethod");
            AsyncBean asyncBean = new AsyncBean();
            asyncBean.asyncMethod();
            System.out.println(Thread.currentThread().getName()+"主线程请求异步执行syncMethod结束");
        }
    }

    @Enable*注解说明

    这些注解主要用来开启对xxx的支持。 此处没有代码示例。

    @EnableAspectJAutoProxy

    开启对AspectJ自动代理的支持。

    @EnableAsync

    开启异步方法的支持。@EnableAsync 开启spring异步执行器,类似xml中的task标签配置(其实是一样的,如果同时存在还会报错),需要联合@Configuration注解一起使用 。

    @EnableScheduling

    开启计划任务的支持。

    @EnableWebMvc

    开启Web MVC的配置支持。

    @EnableConfigurationProperties

    开启对@ConfigurationProperties注解配置Bean的支持。

    @EnableJpaRepositories

    开启对SpringData JPA Repository的支持。

    @EnableTransactionManagement

    开启注解式事务的支持。

    @EnableCaching

    开启注解式的缓存支持。

    定时任务相关

    @EnableScheduling和@Scheduled为SpringBoot注解。这里给出示例代码片段,示例代码中并未有示例。

    @EnableScheduling

    在配置类上使用,开启计划任务的支持(类上)。

    @SpringBootApplication
    @EnableScheduling //开启定时任务
    public class MainApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(MainApplication.class, args);
        }
    }

    @Scheduled

    来申明这是一个任务,包括cron,fixDelay,fixRate等类型(方法上,需先开启计划任务的支持)。

    @Component
    public class Jobs {
        //表示方法执行完成后5秒
        @Scheduled(fixedDelay = 5000)
        public void fixedDelayJob() throws InterruptedException {
            System.out.println("fixedDelay 每隔5秒" + new Date());
        }
    
        //表示每隔3秒
        @Scheduled(fixedRate = 3000)
        public void fixedRateJob() {
    
            System.out.println("fixedRate 每隔3秒" + new Date());
        }
    
        //表示每天8时30分0秒执行
        @Scheduled(cron = "0 0,30 0,8 ? * ? ")
        public void cronJob() {
            System.out.println(new Date() + " ...>>cron....");
        }
    }

    环境切换

    @Profile

    通过设定Environment的ActiveProfiles来设定当前context需要使用的配置环境。

    package com.blackcat.annotation.profiles.config;
    
    import com.alibaba.druid.pool.DruidDataSource;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.EmbeddedValueResolverAware;
    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 org.springframework.util.StringValueResolver;
    
    import javax.sql.DataSource;
    
    /**
     * <p> 描述 : 通过@Profile注解 来根据环境来激活标识不同的Bean
     * @author : blackcat
     * @date  : 2020/5/25 17:45
     *
     * Profile标识在类上,那么只有当前环境匹配,整个配置类才会生效
     * Profile标识在Bean上 ,那么只有当前环境的Bean才会被激活
     * 没有标志为Profile的bean 不管在什么环境都可以被激活
    */
    @Configuration
    @PropertySource(value = {"classpath:ds.properties"})
    public class MyConfig  implements EmbeddedValueResolverAware {
    
        @Value("${ds.username}")
        private String userName;
    
        @Value("${ds.password}")
        private String password;
    
        private String jdbcUrl;
    
        private String classDriver;
    
        @Override
        public void setEmbeddedValueResolver(StringValueResolver stringValueResolver) {
            this.jdbcUrl = stringValueResolver.resolveStringValue("${ds.jdbcUrl}");
            this.classDriver = stringValueResolver.resolveStringValue("${ds.classDriver}");
        }
    
        // 标识为测试环境才会被装配
        @Bean
        @Profile(value = "test")
        public DataSource testDs() {
            return buliderDataSource(new DruidDataSource());
        }
    
        // 标识开发环境才会被激活
        @Bean
        @Profile(value = "dev")
        public DataSource devDs() {
            return buliderDataSource(new DruidDataSource());
        }
    
        // 标识生产环境才会被激活
        @Bean
        @Profile(value = "prod")
        public DataSource prodDs() {
            return buliderDataSource(new DruidDataSource());
        }
    
        private DataSource buliderDataSource(DruidDataSource dataSource) {
            dataSource.setUsername(userName);
            dataSource.setPassword(password);
            dataSource.setDriverClassName(classDriver);
            dataSource.setUrl(jdbcUrl);
            return dataSource;
        }
    }

    切面相关注解

    Spring支持AspectJ的注解式切面编程。这里只写了示例代码,没有controller调用过程。

    @Aspect 

    声明一个切面(类上) 使用@After、@Before、@Around定义建言(advice),可直接将拦截规则(切点)作为参数。

    @PointCut

    声明切点 在java配置类中使用@EnableAspectJAutoProxy注解开启Spring对AspectJ代理的支持(类上)。

    Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。

    @Around

    属于环绕增强,能控制切点执行前,执行后,,用这个注解后,程序抛异常,会影响@AfterThrowing这个注解。

    @AfterReturning

    切点方法返回后执行。后置增强,相当于AfterReturningAdvice,方法正常退出时执行。

    @Before

    标识一个前置增强方法,相当于BeforeAdvice的功能。在切点方法之前执行。

    @AfterThrowing

    切点方法抛异常执行。异常抛出增强,相当于ThrowsAdvice。

    @After

    在切点方法之后执行。 @Before 在方法执行之前执行(方法上) @Around 在方法执行之前与之后执行(方法上)。final增强,不管是抛出异常或者正常退出都会执行。

    代码

    package com.blackcat.annotation.aspect.component;
    
    import org.aspectj.lang.annotation.*;
    import org.springframework.stereotype.Component;
    
    /**
     * <p> 描述 : aop通知注解
     * @author : blackcat
     * @date  : 2020/8/3 16:30
    */
    @Aspect
    @Component
    public class UserAspect {
        /**
         * @Pointcut 定义一个切点,避免重复引用
         */
        @Pointcut("execution(* com.blackcat.annotation.aspect.component.UserServiceImpl.test(..))")
        public void print(){}
    
        @Before("print()")
        public void before(){
            System.out.println("我是前置通知");
        }
    
        @After("print()")
        public void After(){
            System.out.println("我是后置通知");
        }
    
        @AfterReturning("print()")
        public void AfterReturning(){
            System.out.println("我是返回通知");
        }
    
        @AfterThrowing("print()")
        public void AfterThrowing(){
            System.out.println("我是异常通知");
        }
    
    }
    public interface  UserService {
        void test();
    }
    @Service
    public class UserServiceImpl implements UserService  {
    
        @Override
        public void test() {
            System.out.println("测试");
        }
    }
    @Controller
    public class TestAction {
    
        @Autowired
        private UserService userService;
    
        @RequestMapping("/test.do")
        public String info(HttpServletRequest request, HttpServletResponse response){
            userService.test();
            return "index";
        }
    }

    当调用  /test.do  时,会打印一下内容。

    切面相关注解代码示例参考:https://blog.csdn.net/qq_34775355/article/details/88431247

    示例代码

    https://gitee.com/kylin_lawliet/learn-spring/tree/master/annotation

  • 相关阅读:
    404、500、502等HTTP状态码介绍
    Linux系统信息查看命令
    SVN clean up 无法继续
    gitlab使用
    Git SSH Key 生成步骤
    gitlab 杂记
    JS函数
    MySQL基础
    WEB测试方法
    操作系统的发展史
  • 原文地址:https://www.cnblogs.com/Kylin-lawliet/p/13390080.html
Copyright © 2011-2022 走看看