Spring 注解学习 详细代码示例

时间:2023-03-08 15:37:15
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");
}
}

Spring 注解学习 详细代码示例

@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的属性支持

@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 注解学习 详细代码示例

Protetype

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

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

Spring 注解学习 详细代码示例

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 注解学习 详细代码示例

@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 注解学习 详细代码示例

@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 注解学习 详细代码示例

@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 注解学习 详细代码示例

Spring 注解学习 详细代码示例Spring 注解学习 详细代码示例

总结

如果我们注释掉@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 注解学习 详细代码示例

Spring 注解学习 详细代码示例

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

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

Spring 注解学习 详细代码示例

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

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

Spring 注解学习 详细代码示例

Spring 注解学习 详细代码示例

@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 注解学习 详细代码示例

条件注解

以@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 注解学习 详细代码示例

Spring 注解学习 详细代码示例

标注在方法上:

一个方法只能注入一个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 注解学习 详细代码示例

Spring 注解学习 详细代码示例

@ConditionalOnMissingBean

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

Spring 注解学习 详细代码示例

@ConditionalOnClass

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

Spring 注解学习 详细代码示例

Spring 注解学习 详细代码示例

@ConditionalOnMissingClass

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

Spring 注解学习 详细代码示例

Spring 注解学习 详细代码示例

@ConditionalOnExpression

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

Spring 注解学习 详细代码示例

@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 注解学习 详细代码示例

@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 注解学习 详细代码示例

Spring 注解学习 详细代码示例

@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 注解学习 详细代码示例

本节代码示例参考:https://blog.csdn.net/qq_34775355/article/details/88431247

示例代码

https://gitee.com/kylin_lawliet/learn-spring