笔记源码连接:Spring.html(imocc上的排版和html不同,建议看云盘里的源码,自己看完是视频总结的笔记)
Spring
Annotation
前4行被称为元注解
@Target({ElementType.METHOD,ElementType.TYPE}) 作用域,CONSTRUCTOR(构造方法声明),FIELD(字段声明),LOCAL_VARIABLE(局部变量声明),METHOD(方法声明)PACKAGE(包声明),PARAMETER(参数声明),TYPE(类,接口)
@Retention(RetentionPolicy.RUNTIME) 生命周期,SOURCE(只在源码显示,编译时会丢弃),CLASS(编译时会记录到class中,运行时忽略)RUNTIME(运行时存在,可通过反射读取)
@Inherited 允许子类继承
@Documented 生成javadoc时会包含注解
public @Interface Description{ 使用@interface关键字定义注解,成员的类型是受限制的,合法的类型包括原始类型及String,Class,Annotation,Enumeration
String desc(); 成员以无参无异常方式声明,如果注解只有一个成员,则成员名必须取名为Value(),在使用时可以忽略成员名和赋值号(=)
String authot(); 注解可以没有成员,没有成员的注解被称为标识注解
int age() default 18; 可以使用default为成员指定一个默认值
}
Aware
Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean初始化后,可以获得相应的次元,通过Aware接口,可以对Spring相应的资源进行操作
- ApplicationContextAware : 向实现了这个接口的bean提供ioc上下文的信息
- ApplicationEventPublisherAware : 用于事件的发布
- BeanClassLoaderAware : 找到相关的类加载器
- BeanFactoryAware
- BootstrapContextAware
- LoadTimeWeaverAware
- MessageSourceAware : 消息源
- NotificationPublisherAware : 通知
- PortletConfigAware
- ResourceLoaderAware
- ServletConfigAware
- ServletContextAware
Resource
针对于资源文件统一接口
- UrlResource : URL对应的资源,根据一个URL地址即可构建
- ClassPathResource : 获取类路径下的资源文件
- FileSystemResource : 获取文件系统里面的资源
- ServletContextResource : ServletContext封装的资源,用于访问ServletContext环境下的资源
- InputStreamResource : 针对于输入流封装的资源
- ByteArrayResource : 针对于字节数组封装资源
ResourcesLoader
classpath : (classpath:com/myapp/config.xml)
file : (file:/data/config.xml)
http, : (http://myserver/logo.png)
(none) : (/data/config.xml)
@Component,@Repository,@Service,@Controller
- @Component是一个通用注解,可用于任何bean
- @Repository通常用于注解DAO层,即持久层
- @Service通常用于注解Service类,即服务层
- @Controller通常用于注解Controller类,即控制层(MVC)
- 可以自定义bean命名策略,实现BeanNameGenerator接口,并包含一个无参数构造器,如:@Service("myService")
@Scope
@Bean
- @Scope("prototype"),默认是singleton(单例),用于类上
- @Scope(value = "session",proxyMode = ScopeProxyMode.TARGET_CLASS),代理方式
@Required
- @Required适用于bean属性的setter方法
- 这个注解仅仅表示,受影响的bean属性必须在配置时被填充,通过在bean定义或通过自动装配一个明确的属性值
@Autowired
- 可将@Autowired注解理解为"传统"的setter方法
- 可用于构造器或成员变量
- 默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,可以通过设置@Autowired(required=false)的方式避免
- 每个类只有一个构造器被标记为required=ture,@Autowired的必要属性,建议使用@Required注解
- 可以使用@Autowired注解那些众所周知的解析依赖性接口, BeanFactoryAware, ApplicationContext, Environment, ResourcesLoader, ApplicationEventPublisher ,MessageSource
- 可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中所有特定类型的bean
- 可以用于装配key为String的Map
- @Autowired是由Spring BeanPostProcessor处理的,所以不能再自己的BeanPostProcessor类型应用这些注解,这些类型必须通过XML或者Spring的@Bean注解加载
-
@Autowired适用于fields(字段), constructors(构造器), multi-argumentmethods(多参数方法)这些情况下允许在参数级别使用@Qualifier注解缩小范围的情况
@Order - 如果希望数组有序,可以在bean上加@Order注解,如:@Order(value = 1)
@Qualifier
- 按类型自动装配可能多个bean实例的情况,可以使用Spring的@Qualifier注解缩小范围(或指定唯一),也可以用于指定单独的构造器参数或方法的参数
- 可用于注解集合类型变量, 如:@Qualifier("beanimplOne")
@Resource
- 如果通过名字进行注解注入,主要使用的不是@Autowired(即使在技术上能够通过@Qualifier指定bean的名字),替代方式是使用JSR-250@Resource注解,它是通过 其独特的名称来定义来识别特定的目标(这是一个与声明的类型无关的匹配过程)
- 因语义差异,集合或Map类型的bean无法通过@Autowired来注入,以为没有匹配到这样的bean,为这些bean使用@Resource注解,通过唯一名称引用集合或Map的bean
- @Resource适用于成员变量,只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的方式是使用@Qualifier注解
- @Resource有一个name属性,并且默认Spring解释该值作为被注入bean的名称,如 : @Resource(name = "myBean")
- 如果没有显式地指定@Resource的name,默认的名称是从属性名或者setter方法得出
-
注解提供的名字被解析为一个bean的名称,这是由ApplicationContext的中的CommonAnnotationBeanPostProcessor发现并处理的
@Bean - @bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,类似于XML配置文件
- 可以在Spring的@Component注解中使用@Bean注解任何方法(仅仅是可以),二者不常用
- 支持自定义Bean name 如:@Bean(name = "myBean")
- 支持init-method(初始化)destroy-method(销毁) 如:@Bean(intmethod = "A"),初始化时调用方法A,@Bean(destroymethod = "B"),销毁时调用方法B
- 默认@Bean是单例的,可以用@Scope来规定Bean的范围
-
Bean的作用域包括singleton, prototype, request, session, global
@Configuration - @Configuration用在配置文件注解
- 常用的是@Configuration注解在class上,@bean注解在方法上
@ImportResource,@Value
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
publiuc class AppConfig{
@Value("$(jdbc.url)")
private String url;
@Value("$(jdbc.username)")
private String username;
@Value("$(jdbc.password)")
private String password;
@Bean
public DataSource dataSource(){
return new DriverManagerDataSource(url, username, password);
}
}
等于
<beans>
<context:annotation-config />
<context:property-placeholder location="classpath:/com/acme/jdbc.properties" />
<bean class="com.acme.AppConfig"/>
<bean class="org.springframeword.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}" />
<property name="user" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</beans>
@PostConstruct,@PreDestroy
- CommonAnnototionBeanPostProcessor不仅能识别JSR-250中的生命周期注解@Resource,在Spring2.5中引入支持初始化回调和销毁,前提是CommonAnnototionBeanPostProcessor是Spring的ApplicationContext中注册的
- @PostConstruct,@PreDestroy注解在方法上,在初始化,销毁bean都会调用这两个注解注解的方法
@Inject
@Inject等效于@Autowired,可以使用于类,属性,方法,构造器
@Named
- 如果想使用特定名称进行依赖注入,使用@Named
-
@Named与@Component是等效的,和@Qualifier相似
@EnableAspectJAutoProxy - 在Spring配置@AspectJ
@Configuration
@EnableAspectJAutoProxy //自动使用AspectJ进行代理
public class AppConfig{
}
等于
<aop:aspectj-autoproxy/>
@Aspect
- @AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用
- 用@Aspect注解的类可以有方法和字段,他们也可能切入点(pointcut),通知(Advice)和引入(introduction)声明
- @Aspect注解是不能够通过类路径自动检测发现的,所以需要配合@Component注释或者在xml配置bean
-
一个类中的@Aspect注解标识它为一个切面,并且将自己从自动代理类中排除
@Pointcut - 一个切入点通过一个普通的方法定义来提供,并且切入点表达使用@Pointcut注解,方法返回类型必须为void
- 定义一个名为'anyOldTransger',这个切入点将匹配任何名为"transfer"的方法执行
@Pointcut(execution(*transfer(..)))
private void anyOldTransfer(){
}
组合pointcut
-
切入点表达式可以通过 (&& , , != )进行组合,可以以通过名字引入切入点表达式
@Pointcut("execution(public * (..))")private void anyPublicOperation(){
}
@Pointcut("within(com.xyz.someapp.trading..)")
private void inTrading(){}
@Pointcut("anyPublicOperation() && inTrading")
private void trading(){}
Supported Pointcut Designators - execution 匹配方法执行的连接点
- within 限定匹配特定类型的连接点
- this 匹配特定连接点的bean引用是指定类型的实例的限制
- target 限定匹配特定连接点的目标对象是指定类型的实例
- args 限定匹配特定连接点的参数是给定类型的实例
- @target 限定匹配特定连接点的类执行对象的具有给定类型的注解
- @args 限定匹配特定类型的连接点实际传入参数的类型具有给定类型的注解
- @within 限定匹配到内具有给定的注释类型的连接点
- @annotaion 限定匹配特定连接点的主体具有给定的注解
@Before
Before advice
@Component
@Aspect
public class MoocAspect{
@Before("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))")
public void before(){
}
}
@AfterReturning
After returning advice
@Aspect
public class AfterReturningExample{
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck(){
}
}
有时候需要在通知体内得到返回的实际值,可以使用@AfterReturning绑定返回值的形式
@Aspect
public class AfterReturningExample{
@AfterReturning(
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning = "retVal")
public void doAccessCheck(Object retVal){
}
}
@AfterThrowing
After throwing advice
@Aspect
public class AfterThrowingExample{
@AfterThrowing("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doRecoveryActions(){
}
}
有时候需要在通知体内得到返回的实际值,可以使用@AfterReturning绑定返回值的形式
@Aspect
public class AfterThrowingExample{
@AfterThrowing(
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
throwing = "ex")
public void doRecoveryActions(DataAccessException ex){
}
}
@After
After (finally) advice
- 最终通知必须准备处理正常和异常两种返回情况,它通常用于释放资源
@Aspect
public class AfterFinallyExample{
@After("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doReleaseLock(){
}
}
Around advice
- 环绕通知使用@Around注解来声明,通过方法的第一个参数必须是ProceedingJoinPoint类型
- 在通知内部调用ProceedingJoinPoint的proceed()方法会导致执行真正的方法,如果传入一个Object[]对象,数组中的值将被作为参数传递给方法
@Aspect
public class AroundExample{
@Around("com.xyz.myapp.SystemArchitecture.businessService()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
Object retVal = pjp.proceed();
return retVal;
}
}
Advice
Advice传递参数
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation && args(account,..)")
private void validateAccount(Account account){
}
这里的方法Account参数可以是任何类的对象,常用于获取相应的参数,做一些判断,或者日志记录
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation && args(account,..)")
private void accountDataAccessOperatior(Account account){
}
@Before("accountDataAccessOperatior(account)")
public void calidateAccount(Account account){
}
-
作为一种记录,只是记录这个注解应用的一些方法
- 判断方法上是否加了某注解或者方法上加了注解,它对应的值,如下,会根据value的不同,在切面里做不同的操作,前置的判断,分类处理的一种方式,等
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable{
AuditCode value();
}
@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable){
AuditCode code = auditable.value();
}
Advice的参数及泛型
Spring AOP 可以处理泛型类的声明和使用方法的参数
public interface Sample{
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection param);
}
@before("execution( ..Sample+.sampleGenericMethod()) && args(param)")
public void beforeSampleMethod(MyType param){
}
@before("execution( ..Sample+.sampleGenericCollectionMethod()) && args(param)")
public void beforeSampleMethod(Collection param){
}
Advice参数名称
通知和切入点注解有一个额外的"argNames"属性,它可以用来指定所有的方法的参数
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
argNames="bean,auditable)
public void audit(Object bean ,Auditable auditable){
AuditCode code = auditable.value();
}
如果第一个参数是JoinPoint,ProceedingJoinPoint,JoinPoint.StaticPart,那么可以忽略它
@Before(value="com.xyz.lib.Pointcuts.anyPublicMethod() && target(bean) && @annotation(auditable)",
argNames="bean,auditable)
public void audit(,JoinPoint jp ,Object bean ,Auditable auditable){
AuditCode code = auditable.value();
}
Introductions
- 允许一个切面声明一个通知对象实现指定接口,并且提供一个接口实现类来代表这些对象
- introduction使用@DeclareParents进行注解,这个注解用来定义匹配的类型拥有一个新的parent
@DeclareParents
例如:给定一个接口UsageTracked,并且该接口拥有DefaultUsageTracked的实现,接下来的切面声明了所有的service接口的实现都实现了UsageTracked接口
@Aspect
public class UsageTracking{
@DeclareParents(value="com.xyz.myapp.service.*+",defaultImpl=DefaultUsageTracked.class)
public static UsageTracked mixin;
@Before("com.xyz.myapp.SystemArchitecture.businessService() && this(usageTracked)")
public void recordUsage(UsageTracked usageTracked){
usageTracked.incrementUseCount();
}
}
切面实例化模型
- "perthis" 切面通过指定@Aspect注解perthis子句实现
- 每个独立的service对象执行时都会创建一个切面实例
-
service对象的每个方法在第一次执行的时候创建切面实例,切面在service对象失效的同时失效
@Aspect("perthis(com.xyz.myapp.SystemArchitecture.businessService())")
public class MyAspect{
private int someState;
@Before(com.xyz.myapp.SystemArchitecture.businessService())
public void recordServiceUsage(){}
}