一、大纲
1.1 Spring IOC组件
1.2 Spring AOP
1.3 Spring声明式事务
1.4 Spring注解之扩展
二、Spring IOC组件
2.1 Spring IOC注解方式
从Spring3.0 @Configuration
用于定义配置类
,可替换XML配置文件。被注解的类内部包含有一个或者多个@Bean注解的方法,并且这些方法将会被AnnotationConfigApplicationContext
或AnnotationConfigWebApplicationContext
类进行扫描,用于构建Bean定义,初始化Spring容器。
public class AnnotationBeanMethodApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BeanMethodConfigure.class);
// 获取实例
Common common = ctx.getBean("common", Common.class);
OtherCommon otherCommon = ctx.getBean("otherCommon", OtherCommon.class);
// 打印实例
log.info("common = {}", common);
log.info("otherCommon = {}", otherCommon);
ctx.close();
}
}
@Configuration
class BeanMethodConfigure {
// 单实例的销毁是由容器管理
@Bean(value = "common", initMethod = "init", destroyMethod = "destroy")
public Common common() {
return new Common();
}
// 多实例是销毁是由JVM执行
@Scope(scopeName = "prototype")
@Bean(value = "otherCommon", initMethod = "init", destroyMethod = "destroy")
public OtherCommon otherCommon() {
return new OtherCommon();
}
}
2.1 @PostConstruct
@PostConstruct注解的用途:被注解的方法,在对象加载完依赖注入后执行。在构造函数之后,init()方法之前运行。
在Spring项目中,在一个Bean的初始化过程中,方法执行先后顺序
为:Constructor > @Autowired > @PostConstruct。
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PostConstruct {
}
2.2 @PreDestroy
@PreDestroy注解在方法上用作回调通知,表明实例正在被IOC容器删除(IOC容器关闭的时调用销毁)。带有@PreDestroy注解的方法通常用于释放它一直持有的资源。
@Documented
@Retention (RUNTIME)
@Target(METHOD)
public @interface PreDestroy {
}
2.3 @ComponentScan
@ComponentScan
注解通知Spring从哪里开始找到Bean,并且扫描特定注解标识的类载入IOC容器。
2.3.1 注解类
// 注意:虽然直接为ElementType.TYPE,但是只能注解到类上
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 表明:@ComponentScan 可以在类上重复注解使用
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
// 01. 扫描的基础包
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
// 02. 指定多个类或接口的class,扫描时会在这些指定类和接口所属包进行扫描
Class<?>[] basePackageClasses() default {};
// 03. 对应Bean名称的生成器, 默认的是BeanNameGenerator
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
// 04. 提供Bean Scope范围特征解析器
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
// 05. 作用域代理
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
// 06. 控制符合组件检测条件的类文件, 默认是.class文件
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
// 07. 是否自动扫描注解@Component、@Repository、@Service或者@Controller的类
boolean useDefaultFilters() default true;
// 08. 指定包含指定规则的组件
Filter[] includeFilters() default {};
// 09. 指定排除指定规则的组件
Filter[] excludeFilters() default {};
// 10. 扫描到的类是都开启懒加载, 默认false
boolean lazyInit() default false;
}
2.3.2 实现
@Configuration
@ComponentScan
(
// 01. 扫描的基础包
// 02. 指定类, 扫描时会在这些指定类和接口所属包进行扫描
// 03. 指定包含指定规则的组件
// 04. 指定排除指定规则的组件
// 05. 关闭自动扫描注解@Component、@Repository、@Service或者@Controller的类
// 注意:FilterType.CUSTOM需要设置includeFilters = false
basePackages = "com.ioteye.spring.annotation.annotation.component.scan",
basePackageClasses = {OtherService.class},
includeFilters = @Filter(type = FilterType.CUSTOM, classes = PrintTypeFilter.class),
excludeFilters = @Filter(type = FilterType.ANNOTATION, classes = Repository.class),
useDefaultFilters = false
)
public class AnnotationComponentScanConfigure {
}
@Slf4j
class PrintTypeFilter implements TypeFilter {
/**
*
* @param metadataReader 当前扫描类元数据信息Reader
* @param metadataReaderFactory 容器元数据信息的工厂
* @return 如果实例被过滤, 返回false, 否则返回true
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException
{
// 当前扫描类注解信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 当前扫描类元数据信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String className = classMetadata.getClassName();
log.info("className = {}", className);
return true;
}
}
2.4 @Filter
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
// 01. 过滤类型,枚举值为:
// FilterType.ANNOTATION:指定注解
// ASSIGNABLE_TYPE: 指定类的类型
// ASPECTJ: ASPECTJ表达式
// REGEX: 正则表达式
// CUSTOM: 自定义规则, 需要实现TypeFilter接口, 只有在useDefaultFilters=false情况下才能触发
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
// 02. 针对ASPECTJ的值进行设置的
String[] pattern() default {};
}
2.5 @Conditional
@Conditional是Spring4新提供的注解,它的作用是按照一定的条件进行判断,满足条件给容器注册Bean。
2.5.1 注解
// 01. 注解到类上, 满足条件时该类的Bean才会加入容器
// 02. 注解到方法上, 满足条件时该方法的Bean才会加入容器
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
2.5.2 实现
@Configuration
public class AnnotationConditionConfigure {
@Bean
@Conditional(value = WindowsCondititon.class)
public BillGates billGates() {
return new BillGates();
}
class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 01. 获取IOC使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 02. 获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 03. 获取环境信息
Environment environment = context.getEnvironment();
// 04. 获取Bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String env = environment.getProperty("os.name");
// 05. 配置包含Win时才能满足条件
if (env.contains("Win")) {
return true;
}
return false;
}
}
}
2.6 @Import
@Import
只能用在类上,通过快速导入的方式把实例注入到Spring的IOC容器中。
@Import
的三种用法主要包括:
- 直接Class数组
ImportSelector
ImportBeanDefinitionRegistrar
2.6.1 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
2.6.2 实现
@Import(
{
Person.class,
CustomizingImportSelector.class,
CustomizingImportBeanDefinitionRegistrar.class
}
)
public class AnnotationImportConfigure {
}
class CustomizingImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[] {"com.ioteye.spring.annotation.common.entity.BillGates"};
}
}
class CustomizingImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry)
{
RootBeanDefinition definition = new RootBeanDefinition();
definition.setBeanClass(Linus.class);
registry.registerBeanDefinition("linus", definition);
}
}
打印内容:
beanName = org.springframework.context.annotation.internalConfigurationAnnotationProcessor
beanName = org.springframework.context.annotation.internalAutowiredAnnotationProcessor
beanName = org.springframework.context.annotation.internalRequiredAnnotationProcessor
beanName = org.springframework.context.annotation.internalCommonAnnotationProcessor
beanName = org.springframework.context.event.internalEventListenerProcessor
beanName = org.springframework.context.event.internalEventListenerFactory
beanName = annotationImportConfigure
beanName = com.ioteye.spring.annotation.common.entity.Person
beanName = com.ioteye.spring.annotation.common.entity.BillGates
beanName = linus
2.7 @Profile
Spring
框架提供了多profile
管理功能,可以使用@Profile
功能来区分不同环境的配置。激活方式:
- 通过
ConfigurableEnvironment.setActiveProfiles(String...)
以编程方式激活 - 通过
spring.profiles.active
设置JVM系统属性值激活 - 在集成测试中,通过
@ActiveProfiles
注解以声明方式激活配置
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
// 检索Profile类型注释的所有属性
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
// 获取Profile的value属性值
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
}
三、AOP
AOP(Aspect Oriented Programming)称为面向切面编程,在程序开发中主要用来解决一些系统层面上的问题,比如日志,事务,权限等待,Struts2的拦截器设计就是基于AOP的思想,是个比较经典的例子。
3.1 @EnableAspectJAutoProxy
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 注册AspectJAnnotationAutoProxyCreator
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
AnnotationAttributes enableAspectJAutoProxy =
AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
3.2 @Aspect
-
execution
语法
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern) throws-pattern?)
- Spring AOP注解
@Before
:前置建议,在执行一个业务方法之前插入的切面@AfterReturning
:当一个方法正常运行后执行的切面@After
:当方法执行成功或者出现异常的时候都会执行切面@Around
:相当于一个AOP链,如果当前AOP执行后,就让下一个AOP执行@AfterThrowing
,如果在方法中有错误抛出,则执行此建议
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Slf4j
@Aspect
public class LogAop {
@Before("execution(public void com.spring.dao.impl.StudentDaoImpl.*(..))")
public void logBefore() {
log.info("方法执行之前转载日志");
}
@AfterReturning("execution(public void com.spring.dao.impl.StudentDaoImpl.insert(..))")
public void logAfterReturning() {
log.info("方法执行返回后载入日志");
}
@After("execution(public * com.spring.dao.impl.StudentDaoImpl.*(..))")
public void logAfter() {
log.info("finally载入日志");
}
@Around("execution(public * com.spring.dao.impl.StudentDaoImpl.*(..))")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
log.info("around建议载入日志, 时间:{}", new Date());
Object o = pjp.proceed();
return o;
}
@AfterThrowing("execution(public * com.spring.dao.impl.StudentDaoImpl.*(..))")
public void logAfterThrowing() {
log.info("有参数异常载入日志, 时间:{}" + new Date());
}
}
3.3 @Pointcut
标准的Aspectj
Aop的pointcut
的表达式类型是很丰富的,但是Spring Aop只支持其中的9种,外加Spring Aop自己扩充的一种一共是10种类型的表达式,分别如下:
execution
:一般用于指定方法的执行,用的最多。within
:指定某些类型的全部方法执行,也可用来指定一个包。this
:Spring Aop是基于代理的,生成的bean也是一个代理对象,this就是这个代理对象,当这个对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。target
:当被代理的对象可以转换为指定的类型时,对应的切入点就是它了,Spring Aop将生效。args
:当执行的方法的参数是指定类型时生效。@target
:当代理的目标对象上拥有指定的注解时生效。@args
:当执行的方法参数类型上拥有指定的注解时生效。@within
:与@target类似,看官方文档和网上的说法都是@within只需要目标对象的类或者父类上有指定的注解,则@within会生效,而@target则是必须是目标对象的类上有指定的注解。而根据测试这两者都是只要目标类或父类上有指定的注解即可。@annotation
:当执行的方法上拥有指定的注解时生效。bean
:当调用的方法是指定的bean的方法时生效。
@Pointcut("@within(org.springframework.stereotype.Controller)")