Spring 注解学习 使用示例 配置类相关注解 @Bean的属性支持 声明bean的注解 注入bean的注解 @Value注解 条件注解 异步相关 @Enable*注解说明 定时任务相关 环境切换 切面相关注解 示例代码

学习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");
    }
}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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 {

}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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

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

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Protetype

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

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

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

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将在依赖注入完成后被自动调用");
    }
}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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 正在被容器删除");
}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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 +
                '}';
    }
}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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");
    }
}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

总结

如果我们注释掉@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 +
                '}';
    }
}

 Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

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

@Service
public class BaiDuService {

    @Qualifier("userDao")
    @Autowired
    private UserDao userDao2;

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

}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

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

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

 Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

 Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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=电子书

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

条件注解

以@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是否存在。

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

标注在方法上:

一个方法只能注入一个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;
}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@ConditionalOnMissingBean

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

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@ConditionalOnClass

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

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@ConditionalOnMissingClass

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

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@ConditionalOnExpression

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

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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 中,那么我们就可以使用这个注解。

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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结束");
    }
}

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

@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  时,会打印一下内容。

Spring 注解学习 使用示例
配置类相关注解
@Bean的属性支持
声明bean的注解
注入bean的注解
@Value注解
条件注解
异步相关
@Enable*注解说明
定时任务相关
环境切换
切面相关注解
示例代码

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

示例代码

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