第1章 概述
1-1 Spring入门课程简介
- 什么是框架
- Spring简介
- IOC(配置、注解)
- Bean(配置、注解)
- AOP(配置、注解、AspectJ、API)
SpringFramework
Spring Expression Language(SpEl)
Spring Integration
Spring Web Flow
Spring Security
Spring Data
Spring Batch
- 掌握用法
- 深入理解
- 不断实践
- 反复总结
再次深入理解与实践
- http://projects.spring.io/spring-framework
- 其他
1-2 Spring概况
- Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,
但现在已经不止应用于企业应用 - 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架
- 从大小开销两方面而言spring都是轻量的
- 通过控制反转的技术(IoC)达到了松耦合的目的
- 提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑
与系统级服务进行内聚性的开发 - 包含并管理应用对象的配置和生命周期,这个意义上是一种容器
- 将简单的组件配置、组合成复杂的应用,这个意义上是框架
为什么是Spring
- 在Spring上开发应用简单
- 在Spring上开发应用方便
- 在Spring上开发应用快捷
Spring带来了复杂JavaEE开发的春天
Spring Framework Runtime
- Test
- Core Contianer
- Beans
- Core
- Context
- SpEL
- AOP/Aspects/Instrumentation/Messaging
- Data Acess/Integration
- JDBC
- ORM
- OXM
- JMS
- Transcations
Web
- WebSocket
- Servlet
- Web
- Portlet
容器
- 提供了对多种技术的支持
- JMS
- MQ支持
- UnitTest
- …
- AOP(事务管理、日志等)
- 提供了众多方便应用的辅助类(JDBC Template等)
- 对主流应用框架(Hibernate)提供了良好的支持
适用范围
- 构建企业应用(SpringMVC+Spring+Hibernate/ibatis)
- 单独使用Bean容器(Bean管理)
- 单独使用AOP进行切面处理
- 其他的Spring功能,如:对消息的支持等
- 在互联网中的应用…
1-3 Spring框架
- 什么是框架
- 为什么使用框架
什么是框架
- 维基百科:软件框架,通常是指为了实现某个业界标准或者完成特定
基本任务的软件组件规范,也指为了实现某个软件组织规范时,提供
规范所要求之基础功能的软件产品 框架就是制定一套规范或者规则(思想),大家(程序员)在该规范
或者规程(思想)下工作。或者说就是使用别人搭好的舞台,你来做
表演。框架的特点
- 半成品
- 封装了特定的处理流程和控制逻辑
- 成熟的、不断升级的软件
- 框架与类库的区别
- 框架一般是封装了逻辑、高内聚的,类库则是松散的工具组合
- 框架专注于某一领域,类库则是更通用的
为什么使用框架
- 软件系统日趋复杂
- 重用度高,开发效率和质量提高
- 软件设计人员要专注于对领域的了解,使需求分析更充分
- 易于上手、快速解决问题
第2章 Spring IOC容器
2-1 IOC及Bean容器
- 接口及面向接口编程
- 什么是IOC
- Spring的Bean配置
- Bean的初始化
- Spring的常用注入方式
接口
- 用于沟通的中介物的抽象化
- 实体把自己提供给外界的一种抽象化说明,用以由内部操作分离出
外部沟通方式,使其能被修改内部而不影响外界其他实体与其交互
的方式 - 对应Java接口即声明,声明了哪些方法对外提供的
- 在Java8中,接口可以拥有方法体
面向接口编程
- 结构设计中,分清层次及调用关系,每层只向外(上层)提供一组
功能接口,各层间仅依赖接口而非实现类 - 接口实现的变动不影响各层间的调用,这一点在公共服务中尤为重要
- “面向接口编程”中的“接口”是用于隐藏具体实现和实现多态性的组件
-例子
什么是IOC
- IOC:控制反转,控制权的转移,应用本身不负责依赖对象的创建和
维护,而是由外部容器负责创建和维护 - DI(依赖注入)是其中一种实现方式
- 目的:创建对象并且组装对象之间的关系
扩展理解
- 2004年,Martin Fowler探讨了同一个问题,既然IoC是控制反转,那么
到底是“哪些方面被控制反转了呢?”,经过详细的分析和论证后,他得
出了答案:“获得依赖对象的过程被反转了”。控制被反转之后,获得依赖
对象的过程由自身管理变成了由IOC容器的主动注入。于是,他给“控制反转”
取了一个更合适的名字“依赖注入(Dependency Injection)”。他的这个
答案,实际上给出了实现IoC的方法:注入。所谓依赖注入,就是由IoC容器
在运行期间,动态地将某种依赖关系注入到对象之中。
IOC房屋中介
- 找中介
- 中介介绍房子
- 租房、入住
IOC
- 找IOC容器
- 容器返回对象
- 使用对象
Spring的Bean配置
- 刚才的接口在Spring中的配置方式
- 例子
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="oneInterface" class="com.imooc.ioc.interfaces.OneInterfaceImpl"></bean>
</beans>
```
单元测试
- 下载junit-*.jar并引入工程
- 创建UnitTestBase类,完成对Spring配置文件的加载、销毁
- 所有的单元测试类都继承自UnitTestBase,通过它的getBean
方法获取想要得到的对象
- 子类(具体执行单元测试的类)加注解:
@RunWith(BlockJUnit4ClassRunner.class)
- 单元测试方法加注解:@Test
- 右键选择要执行的单元测试方法或者执行一个类的全部单元测试方法
Bean容器初始化
- 基础:两个包
- org.springframework.beans
- org.springframework.context
- BeanFactory提供配置结构和基本功能,加载并初始化Bean
- ApplicationContext保存了Bean对象并在Spring中广泛使用
- 方式,ApplicationContext
- 本地文件
- Classpath
- Web应用中依赖servlet或Listener
- 文件
FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext("F:/workspace/appcontext.xml");
- Classpath
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:spring-context.xml");
- Web应用
```xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderServlet</listener-class>
</listener>
<servlet>
<servlet-name>context</servlet-name>
<servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
<load-on-startup>on</load-on-startup>
</servlet>
<div class="se-preview-section-delimiter"></div>
public class UnitTestBase {
private ClassPathXmlApplicationContext context;
private String springXmlpath;
public UnitTestBase() {
}
public UnitTestBase(String springXmlpath) {
this.springXmlpath = springXmlpath;
}
@Before
public void before() {
if (StringUtils.isEmpty(springXmlpath)) {
springXmlpath = "classpath*:spring-*.xml";
}
try {
context = new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+"));
context.start();
} catch (BeansException e) {
e.printStackTrace();
}
}
@After
public void after() {
context.destroy();
}
@SuppressWarnings("unchecked")
protected <T extends Object> T getBean(String beanId) {
try {
return (T)context.getBean(beanId);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
protected <T extends Object> T getBean(Class<T> clazz) {
try {
return context.getBean(clazz);
} catch (BeansException e) {
e.printStackTrace();
return null;
}
}
}
<div class="se-preview-section-delimiter"></div>
public interface OneInterface {
public void say();
}
<div class="se-preview-section-delimiter"></div>
public class OneInterfaceImpl implements OneInterface {
@Override
public void say() {
System.out.println("oneInterfaceImpl say ...");
}
}
<div class="se-preview-section-delimiter"></div>
@RunWith(BlockJUnit4ClassRunner.class)
public class TestOneInterface extends UnitTestBase {
public TestOneInterface(){
super("classpath:spring-ioc.xml");
}
@Test
public void test(){
OneInterface onInterface = super.getBean("oneInterface");
onInterface.say();
}
}
<div class="se-preview-section-delimiter"></div>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="oneInterface" class="com.imooc.ioc.interfaces.OneInterfaceImpl"></bean>
</beans>
```
<div class="se-preview-section-delimiter"></div>
###### 2-2 Spring注入方式
- Spring注入是指在启动Spring容器加载bean配置的时候,完成对变量的赋值行为
- 常用的两种注入方式
- 设置注入
- 构造注入
<div class="se-preview-section-delimiter"></div>
```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl">
<property name="injectionDAO" ref="injectionDAO"></property>
</bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="injectionService" class="com.imooc.ioc.injection.service.InjectionServiceImpl">
<constructor-arg name="injectionDAO" ref="injectionDAO"></constructor-arg>
</bean>
<bean id="injectionDAO" class="com.imooc.ioc.injection.dao.InjectionDAOImpl"></bean>
</beans>
public interface InjectionDAO {
public void save(String arg);
}
public class InjectionDAOImpl implements InjectionDAO {
public void save(String arg) {
//模拟数据库保存操作
System.out.println("保存数据:" + arg);
}
}
public interface InjectionService {
public void save(String arg);
}
public class InjectionServiceImpl implements InjectionService {
private InjectionDAO injectionDAO;
//设值注入
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
public void save(String arg) {
//模拟业务操作
System.out.println("Service接收参数:" + arg);
arg = arg + ":" + this.hashCode();
injectionDAO.save(arg);
}
}
public class InjectionServiceImpl implements InjectionService {
private InjectionDAO injectionDAO;
//构造器注入
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
public void save(String arg) {
//模拟业务操作
System.out.println("Service接收参数:" + arg);
arg = arg + ":" + this.hashCode();
injectionDAO.save(arg);
}
}
````
<div class="se-preview-section-delimiter"></div>
```java
@RunWith(BlockJUnit4ClassRunner.class)
public class TestInjection extends UnitTestBase {
public TestInjection() {
super("classpath:spring-injection.xml");
}
// @Test
// public void testSetter() {
// InjectionService service = super.getBean("injectionService");
// service.save("这是要保存的数据");
// }
@Test
public void testCons() {
InjectionService service = super.getBean("injectionService");
service.save("这是要保存的数据");
}
}
第3章 Spring Bean装配(上)
3-1 Spring Bean装配之Bean的配置项及作用域
Bean
- Bean配置项
- Bean的作用域
- Bean的生命周期
- Bean的自动装配
- Resources&ResourcesLoader
Bean配置项
- Id
- Class
- Scope
- Constructor argument
- Properties
- Autowiring mode
- lazy-initialization mode
- Initialization/destruction method
Bean的作用域
- singleton:单例,指一个Bean容器中只存在一份
- prototype:每次请求(每次使用)创建新的实例,destroy方式不生效
- request:每次http请求创建一个实例且仅在当前request内有效
- session:同上,每次http请求创建,当前session内有效
- global session:基于portlet的web中有效(portlet定义了global session),如果在web中,通session
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="beanScope" class="com.imooc.bean.BeanScope" scope="prototype"></bean>
</beans>
public class BeanScope {
public void say() {
System.out.println("BeanScope say : " + this.hashCode());
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanScope extends UnitTestBase {
public TestBeanScope() {
super("classpath*:spring-beanscope.xml");
}
@Test
public void testSay() {
BeanScope beanScope = super.getBean("beanScope");
beanScope.say();
BeanScope beanScope2 = super.getBean("beanScope");
beanScope2.say();
}
@Test
public void testSay2() {
BeanScope beanScope = super.getBean("beanScope");
beanScope.say();
}
}
3-2 Spring Bean装配之Bean的生命周期
生命周期
- 定义
- 初始化
- 使用
- 销毁
初始化
- 实现org.springframework.beans.factory.InitializingBean接口,覆盖afterPropertiesSet方法
- 配置init-method
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"></bean>
public class ExampleBean{
public void init(){
//do some initialization work
}
}
public class ExampleInitializingBean implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception{
//do something
}
}
销毁
- 实现org.springframework.beans.factory.DisposableBean接口,覆盖destory方法
- 配置destory-method
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"></bean>
public class ExampleBean{
public void cleanup(){
//do some destruction work(like releasing pooled connections)
}
}
public class ExampleDisposableBean implements DisposableBean{
@Override
public void destroy() throws Exception{
//do something
}
}
配置全局默认初始化、销毁方法
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-init-method="defautInit" default-destroy-method="defaultDestroy">
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-init-method="defautInit" default-destroy-method="defaultDestroy">
<bean id="beanLifeCycle" class="com.imooc.lifecycle.BeanLifeCycle" init-method="start" destroy-method="stop"></bean>
</beans>
public class BeanLifeCycle implements InitializingBean, DisposableBean {
public void defautInit() {
System.out.println("Bean defautInit.");
}
public void defaultDestroy() {
System.out.println("Bean defaultDestroy.");
}
@Override
public void destroy() throws Exception {
System.out.println("Bean destroy.");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("Bean afterPropertiesSet.");
}
public void start() {
System.out.println("Bean start .");
}
public void stop() {
System.out.println("Bean stop.");
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanLifecycle extends UnitTestBase {
public TestBeanLifecycle() {
super("classpath:spring-lifecycle.xml");
}
@Test
public void test1() {
super.getBean("beanLifeCycle");
}
}
3-3 Spring Bean装配之Aware接口
Aware
- Spring中提供了一些以Aware结尾的接口,实现了Aware接口的bean在初始化之后,
可以获取相应资源 - 通过Aware接口,可以对Spring相应资源进行操作(一定要慎重)
为对Spring进行简单的扩展提供了方便的入口
ApplicationContextAware:When an ApplicationContext creates a class that implements
the org.springframework.context.ApplicationContextAware interface,the class is
provided with a reference to that ApplicationContext- BeanNameAware:When an ApplicationContext creates a class that implements the
org.springframework.beans.factory.BeanNameAware interface,the class is provided
with a reference to the name defined in its associated object definition
Name | Injected Dependency |
---|---|
ApplicationContextAware | Declaring ApplicationContext |
ApplicationEventPublisherAware | Event publisher of the enclosing ApplicationContext |
BeanClassLoaderAware | Class loader used to load the bean classes |
BeanFactoryAware | Declaring BeanFactory |
BeanNameAware | Name of the declaring bean |
BootstrapContextAware | Resourec adapter BootstrapContext the container runs in. Typically available only JCA aware ApplicationContexts |
LoadTimeWeaverAware | Defined weaver for processing class definition at load time |
ServletConfigAware | Current ServletConfig the contianer runs Valid only in a web-aware Spring ApplicationContext |
ServletContextAware | Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext |
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<!-- <bean id="moocApplicationContext" class="com.imooc.aware.MoocApplicationContext" ></bean> -->
<bean id="moocBeanName" class="com.imooc.aware.MoocBeanName" ></bean>
</beans>
```
```java
public class MoocApplicationContext implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
System.out.println("MoocApplicationContext : " + applicationContext.getBean("moocApplicationContext").hashCode());
}
}
<div class="se-preview-section-delimiter"></div>
public class MoocBeanName implements BeanNameAware, ApplicationContextAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println("MoocBeanName : " + name);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
System.out.println("setApplicationContext : " + applicationContext.getBean(this.beanName).hashCode());
}
}
<div class="se-preview-section-delimiter"></div>
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAware extends UnitTestBase {
public TestAware() {
super("classpath:spring-aware.xml");
}
// @Test
// public void testMoocApplicationContext() {
// System.out.println("testMoocApplicationContext : " + super.getBean("moocApplicationContext").hashCode());
// }
@Test
public void textMoocBeanName() {
System.out.println("textMoocBeanName : " + super.getBean("moocBeanName").hashCode());
}
}
<div class="se-preview-section-delimiter"></div>
3-4 Spring Bean装配之自动装配
Bean的自动装配
- No:不做任何操作
- byName:根据属性名自动装配。此选项将检查容器并根据名字查找与属性完全一致的bean,并将其与属性自动装配
- byType:如果容器中存在一个与指定属性类型相同的bean,那么将于该属性自动装配;如果存在多个该类型bean,那么
抛出异常,并指出不能使用byType方式进行自动装配;如果没有找到相匹配的bean,则什么事都不发生 - constructor:与byType方式类似,不同之处在于它应用于构造器参数。如果容器中没有找到与构造器参数一致的bean,
那么抛出异常。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd"
default-autowire="constructor">
<bean id="autoWiringService" class="com.imooc.autowiring.AutoWiringService" ></bean>
<bean class="com.imooc.autowiring.AutoWiringDAO" ></bean>
</beans>
<div class="se-preview-section-delimiter"></div>
public class AutoWiringDAO {
public void say(String word) {
System.out.println("AutoWiringDAO : " + word);
}
}
<div class="se-preview-section-delimiter"></div>
public class AutoWiringService {
private AutoWiringDAO autoWiringDAO;
public AutoWiringService(AutoWiringDAO autoWiringDAO) {
System.out.println("AutoWiringService");
this.autoWiringDAO = autoWiringDAO;
}
public void setAutoWiringDAO(AutoWiringDAO autoWiringDAO) {
System.out.println("setAutoWiringDAO");
this.autoWiringDAO = autoWiringDAO;
}
public void say(String word) {
this.autoWiringDAO.say(word);
}
}
<div class="se-preview-section-delimiter"></div>
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAutoWiring extends UnitTestBase {
public TestAutoWiring() {
super("classpath:spring-autowiring.xml");
}
@Test
public void testSay() {
AutoWiringService service = super.getBean("autoWiringService");
service.say(" this is a test");
}
}
<div class="se-preview-section-delimiter"></div>
3-5 Spring Bean装配之Resource
- 针对于资源文件的统一接口
Resources
- UrlResources:URL对应的资源,根据一个URL地址即可构建
- ClassPathResource:获取类路径下的资源文件
- FileSystemResource:获取文件系统里面的资源
- ServletContextResource:ServletContext封装的资源,用于访问ServletContext环境下的资源
- InputStreamResource:针对于输入流封装的资源
- ByteArrayResource:针对于字节数组封装的资源
ResourceLoader
- All application contexts implement the ResourceLoader interface,
and therefore all application contexts may be used to obtain Resource instances.
- All application contexts implement the ResourceLoader interface,
public interface ResourceLoader{
Resource getResource(String location);
}
Resource templte = ctx.getResource(“some/resource/path/myTemplate.txt”);
Resource templte = ctx.getResource(“classpath:some/resource/path/myTemplate.txt”);
Resource templte = ctx.getResource(“file:/some/resource/path/myTemplete.txt”);
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd" >
<bean id="moocResource" class="com.imooc.resource.MoocResource" ></bean>
</beans>
<div class="se-preview-section-delimiter"></div>
public class MoocResource implements ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = applicationContext;
}
public void resource() throws IOException {
Resource resource = applicationContext.getResource("config.txt");
System.out.println(resource.getFilename());
System.out.println(resource.contentLength());
}
}
<div class="se-preview-section-delimiter"></div>
@RunWith(BlockJUnit4ClassRunner.class)
public class TestResource extends UnitTestBase {
public TestResource() {
super("classpath:spring-resource.xml");
}
@Test
public void testResource() {
MoocResource resource = super.getBean("moocResource");
try {
resource.resource();
} catch (IOException e) {
e.printStackTrace();
}
}
}
<div class="se-preview-section-delimiter"></div>
第4章 Spring Bean装配(下)
4-1 Spring Bean装配之Bean的定义及作用域的注解实现
- Classpath扫描与组件管理
- 类的自动检测与注册Bean
- @Component,@Repository,@Service,@Controller
- @Required
- @Autowired
- @Qualifier
@Resource
从Spring3.0开始,Spring JavaConfig项目提供了很多特性,包括使用
java而不是XML定义bean,比如@Configuration,@Bean,@Import,@DependsOn- @Component是一个通用注解,可用于任何bean
- @Repository,@Service,@Controller是更具针对性的注解
- @Repository通常用于注解DAO类,即持久层
- @Service通常用于Service类,及服务层
- @Controller通常用于Controller类,及控制层(MVC)
元注解(Meta-annotations)
- 许多Spring提供的注解可以用作为自己的代码,即“元数据注解”,元注解是
一个简单的注解,可以应用到另一个注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //Spring will see this and treat @Service in the same way sa @Component
public @interface Service{
//...
}
<div class="se-preview-section-delimiter"></div>
- 除了value(),元注解还可以有其它的属性,允许定制
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope("session")
public @interface SessionScope{
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT
<div class="se-preview-section-delimiter"></div>
类的自动检测及Bean的注解
- Spring可以自动检测类并注册Bean到ApplicationContext中
@Service
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Autowired
public SimpleMovieLister(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
}
<div class="se-preview-section-delimiter"></div>
@Repository
public class JpaMovieFinder implements MovieFinder{
//implementation elided for clarity
}
<div class="se-preview-section-delimiter"></div>
- 通过在基于XML的Spring配置如下标签(请注意包含上下文命名空间)
- 仅会查找在同一个applicationcontext
中的bean注解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:annotation-config/>
</beans>
<div class="se-preview-section-delimiter"></div>
类的自动检测及Bean的注册
- 为了能够检测这些类并注册相应的Bean,需要下面内容
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="org.example"></context:component-scan>
</beans>
<div class="se-preview-section-delimiter"></div>
- 包含,通常在使用前者后,不用再使用后者
- AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor也会被包含进来
使用过滤器进行自定义扫描
- 默认情况下,类自动发现并注册bean的条件是:使用@Component,@Repository,@Service,@Controller注解
或者使用@Component的自定义注解 - 可以通过过滤器修改上面的修为,如:下面例子的XML配置忽略所有的@Repository注解并用“Stub”代替
<beans>
<context:component-scan base-package="">
<context:include-filter type="regex"
expression=".*Stub.*Repository"/>
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Repository" />
</context:component-scan>
</beans>
- 还可以使用use-default-filters = "false" 禁用自动发现与注册
Using filters to customize scanning
| Filter Type | Example Expression | Description |
| :------: | :------: | :------: |
| annotation | org.example.SomeAnnotation | An annotation to be present at the type level in target components. |
| assignable | org.example.SomeClass | A class (or interface) that the target components are assignable to (extend/implement). |
| aspectj | org.example..*Service+ | An AspectJ type expression to be matched by the target components. |
| regex | org\.example\.Default.* | A regex expression to be matched by the target components class names. |
| custom | org.example.MyTypeFilter | A custom implementation of the org.springframework.core.type.TypeFilter interface. |
定义Bean
- 扫描过程中组件自动检测,那么Bean名称是由BeanNameGenerator生成的(@Component,
@Repository,@Service,@Controller都会有一个那么属性用于显示设置Bean Name)
<div class="se-preview-section-delimiter"></div>
```java
@Service("myMovieLister")
public class SimpleMovieLister{
//...
}
@Repository
public class MovieFinderImpl implements MovieFinder{
//...
}
- 可自定义bean命名策略,实现BeanNameGenerator接口,并一定要包含一个无参构造器
<beans>
<context:component-scan base-package="org.example"
name-generator="org.example.MyNameGenerator" />
</beans>
作用域(Scope)
- 通常情况下自动查找的Spring组件,其中scope是singleton,Spring2.5提供了一个标识scope
的注解@Scoped
```java
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder{
//...
}
<div class="se-preview-section-delimiter"></div>
- 也可以自定义scope策略,实现ScopeMetadataResolver接口并提供一个无参构造器
<beans>
<context:component-scan base-package="org.example"
scope-resolver="org.example.MyScopeResolver" />
</beans>
代理方式
- 可以使用scoped-proxy属性指定代理,有三个值可选:no,interfaces,targetClass
<div class="se-preview-section-delimiter"></div>
```xml
<beans>
<context:component-scan base-package="org.example"
scoped-proxy="interfaces" />
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannotation"></context:component-scan>
</beans>
//@Component("bean")
@Scope
@Component
public class BeanAnnotation {
public void say(String arg) {
System.out.println("BeanAnnotation : " + arg);
}
public void myHashCode() {
System.out.println("BeanAnnotation : " + this.hashCode());
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanAnnotation extends UnitTestBase {
public TestBeanAnnotation() {
super("classpath:spring-beanannotation.xml");
}
@Test
public void testSay() {
BeanAnnotation bean = super.getBean("beanAnnotation");
bean.say("This is test.");
bean = super.getBean("bean");
bean.say("This is test.");
}
@Test
public void testScpoe() {
BeanAnnotation bean = super.getBean("beanAnnotation");
bean.myHashCode();
bean = super.getBean("beanAnnotation");
bean.myHashCode();
}
}
4-2 Spring Bean装配之Autowired注解说明-1
@Required
- @Require注解适用于bean属性的setter方法
- 这个注解仅仅表示,受影响的bean属性必须在配置时被填充,
通过在bean定义或者通过自动装配一个明确的属性值
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
//...
}
@Autowired
- 可以将@Autowired注解为“传统”的setter方法
```java
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
<div class="se-preview-section-delimiter"></div>
- 可用于构造器或成员变量
@Autowired
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao){
this.customerPreferenceDao = customerPreferenceDao;
}
<div class="se-preview-section-delimiter"></div>
- 默认情况下,如果因找不到合适的bean将会导致autowiring失败抛出异常,
可以通过下面方式避免
public class SimpleMovieLister{
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
//...
}
<div class="se-preview-section-delimiter"></div>
public interface InjectionDAO {
public void save(String arg);
}
<div class="se-preview-section-delimiter"></div>
@Repository
public class InjectionDAOImpl implements InjectionDAO {
public void save(String arg) {
//模拟数据库保存操作
System.out.println("保存数据:" + arg);
}
}
<div class="se-preview-section-delimiter"></div>
public interface InjectionService {
public void save(String arg);
}
<div class="se-preview-section-delimiter"></div>
@Service
public class InjectionServiceImpl implements InjectionService {
// @Autowired
private InjectionDAO injectionDAO;
@Autowired
public InjectionServiceImpl(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
// @Autowired
public void setInjectionDAO(InjectionDAO injectionDAO) {
this.injectionDAO = injectionDAO;
}
public void save(String arg) {
//模拟业务操作
System.out.println("Service接收参数:" + arg);
arg = arg + ":" + this.hashCode();
injectionDAO.save(arg);
}
}
<div class="se-preview-section-delimiter"></div>
@RunWith(BlockJUnit4ClassRunner.class)
public class TestBeanAnnotation extends UnitTestBase {
public TestBeanAnnotation() {
super("classpath:spring-beanannotation.xml");
}
@Test
public void testSay() {
BeanAnnotation bean = super.getBean("beanAnnotation");
bean.say("This is test.");
bean = super.getBean("bean");
bean.say("This is test.");
}
@Test
public void testScpoe() {
BeanAnnotation bean = super.getBean("beanAnnotation");
bean.myHashCode();
bean = super.getBean("beanAnnotation");
bean.myHashCode();
}
}
<div class="se-preview-section-delimiter"></div>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannotation"></context:component-scan>
</beans>
```
<div class="se-preview-section-delimiter"></div>
###### 4-3 Spring Bean装配之Autowired注解说明-2
@Autowired
- 可以使用@Autowired注解那些众所周知的解析依赖性接口,比如:BeanFactory,ApplicationContext,
Environment,ResourceLoader,ApplicationEventPublisher,and MessageSource
<div class="se-preview-section-delimiter"></div>
```java
public class MovieRecommender{
@Autowired
private ApplicationContext context;
public MovieRecommender(){
}
//...
}
- 可以通过添加注解给需要该类型的数组的字段或方法,以提供ApplicationContext中
所有特定类型的bean
private Set<MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs){
this.movieCatalogs = movieCatalogs;
}
- 可以用于装配key为String的Map
private Map<String,MovieCatalog> movieCatalogs;
@Autowired
public void setMovieCatalogs(Map<String,MovieCatalog> movieCatalogs){
this.movieCatalogs = movieCatalogs;
}
- 如果希望数组有序,可以让bean实现org.springframework.core.Ordered接口或使用的@Order注解
- @Autowired是由Spring BeanPostProcessor处理的,所有不能在自己的BeanPostProcessor或
BeanFactoryPostProcessor类型应用这些注解,这些类型必须通过XML或者Spring的@Bean注解加载
4-4 Spring Bean装配之Autowired注解说明-3
@Qualifier
- 按类型装配可能多个bean实例的情况,可以使用Spring的@Qualifier注解缩小范围(或指定唯一),
也可以用于指定单独的构造器参数或方法参数 - 可用于注解集合类型变量
public class MovieRecommender{
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
}
public class MovieRecommender{
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(@Qualifier("main")MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao){
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
//...
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/>
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/>
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"></bean>
</beans>
- 如果通过名字进行注解注入,主要使用的不是@Autowired(即使在技术上可以通过@Qualifier指定bean的名字),
替代方式是使用JSR-250@Resource注解,它是通过其独特的名称来定义来识别特定的目标(这是一个与声明
类型无关的匹配过程) 因语义差异,集合或Map类型的bean无法通过@Autowired来注入,因为没有类型匹配到这样的bean,为这些bean
使用@Resource注解,通过唯一名称引用集合或Map的bean@Autowired适用于fields,construtors,multi-argument methods这些允许在参数级别使用@Qualifier注解
缩小范的情况@Resource适用于成员变量、只有一个参数的setter方法,所以在目标是构造器或一个多参数方法时,最好的
方式是使用qualifier定义自己的qualifier注解并使用
@Target({ElementType.FIELD,ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @inferface Genre{
String value();
}
@Autowired
@Genre("Action")
private MovieCatalog anctionCatalog;
private MovieCatalog comedyCatalog;
@Autowired
public void setComedyCatalog(@Genre("Comedy") MovieCatalog comedyCatalog){
this.comedyCatalog = comedyCatalog;
}
//...
}
```xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier type="Genre" value="Action"/>
</bean>
<bean class="example.SimpleMovieCatalog">
<qualifier type="example.Genre" value="Comedy"/>
</bean>
<bean id="movieRecommender" class="example.MovieRecommender"></bean>
</beans>
<div class="se-preview-section-delimiter"></div>
public interface BeanInterface {
}
<div class="se-preview-section-delimiter"></div>
@Order(2)
@Component
public class BeanImplOne implements BeanInterface {
}
<div class="se-preview-section-delimiter"></div>
@Order(1)
@Component
public class BeanImplTwo implements BeanInterface {
}
<div class="se-preview-section-delimiter"></div>
@Component
public class BeanInvoker {
@Autowired
private List<BeanInterface> list;
@Autowired
private Map<String, BeanInterface> map;
@Autowired
@Qualifier("beanImplTwo")
private BeanInterface beanInterface;
public void say() {
if (null != list && 0 != list.size()) {
System.out.println("list...");
for (BeanInterface bean : list) {
System.out.println(bean.getClass().getName());
}
} else {
System.out.println("List<BeanInterface> list is null !!!!!!!!!!");
}
System.out.println();
if (null != map && 0 != map.size()) {
System.out.println("map...");
for (Map.Entry<String, BeanInterface> entry : map.entrySet()) {
System.out.println(entry.getKey() + " " + entry.getValue().getClass().getName());
}
} else {
System.out.println("Map<String, BeanInterface> map is null !!!!!!!!!!");
}
System.out.println();
if (null != beanInterface) {
System.out.println(beanInterface.getClass().getName());
} else {
System.out.println("beanInterface is null...");
}
}
}
<div class="se-preview-section-delimiter"></div>
@RunWith(BlockJUnit4ClassRunner.class)
public class TestInjection extends UnitTestBase {
public TestInjection() {
super("classpath:spring-beanannotation.xml");
}
@Test
public void testMultiBean() {
BeanInvoker invoker = super.getBean("beanInvoker");
invoker.say();
}
}
<div class="se-preview-section-delimiter"></div>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannotation"></context:component-scan>
</beans>
<div class="se-preview-section-delimiter"></div>
4-5 Spring Bean装配之基于Java的容器注解说明——@Bean
基于java的容器注解
- @Bean标识一个用于配置和初始化一个由SpringIoC容器管理的新对象的方法,
类似于XML配置文件中的 - 可以在Spring的@Conponent注解的类中使用@Bean注解任何方法(仅仅是可以)
- 上一点中,通常使用的是@Configuration
@Configuration
public class AppConfig{
@Bean
public MyService myService(){
return new MyServiceImpl();
}
}
<div class="se-preview-section-delimiter"></div>
相当于:
<beans>
<bean id="myService" class="com.acme.services.MyServiceImpl" />
</beans>
<div class="se-preview-section-delimiter"></div>
- 自定义Bean name
@Configuration
public class AppConfig{
@Bean(name = "myFoo")
public Foo foo(){
return new Foo();
}
}
<div class="se-preview-section-delimiter"></div>
- init-method
- destroy-method
public class Foo{
public void init(){
//initialization logic
}
}
<div class="se-preview-section-delimiter"></div>
public class Bar{
public void cleanup(){
//destruction logic
}
}
<div class="se-preview-section-delimiter"></div>
@Configuration
public class AppConfig{
@Bean(initMethod="init")
public Foo foo(){
return new Foo();
}
@Bean(destoryMethod="cleanup")
public Bar bar(){
return new Bar();
}
}
<div class="se-preview-section-delimiter"></div>
- 例子
public interface Store {
}
<div class="se-preview-section-delimiter"></div>
public class StringStore implements Store{
}
<div class="se-preview-section-delimiter"></div>
@Configuration
public class StoreConfig {
@Bean
public Store getStringStore(){
return new StringStore();
}
}
<div class="se-preview-section-delimiter"></div>
@RunWith(BlockJUnit4ClassRunner.class)
public class TestStoreConfig extends UnitTestBase {
public TestStoreConfig(){
super("classpath*:spring-beanannitation.xml");
}
@Test
public void test(){
Store store = super.getBean("getStringStore");
System.out.println(store.getClass().getName());
}
}
<div class="se-preview-section-delimiter"></div>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannitation"></context:component-scan>
</beans>
```
<div class="se-preview-section-delimiter"></div>
###### 4-6 Spring Bean装配之基于Java的容器注解说明——@ImportResource和@Value
@ImportResource和@Value注解进行资源文件的读取
<div class="se-preview-section-delimiter"></div>
```xml
<beans>
<context:annotation-config/>
<context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
<bean class="com.acme.AppConfig"/>
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
</beans>
@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public 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);
}
}
- 例子
public class MyDriverManager {
public MyDriverManager(String url,String username,String password){
System.out.println(url);
System.out.println(username);
System.out.println(password);
}
}
@Configuration
@ImportResource("classpath:config.xml")
public class StoreConfig {
@Value("${url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${password}")
private String password;
@Bean
public MyDriverManager myDriverManager(){
return new MyDriverManager(url,username,password);
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestStoreConfig extends UnitTestBase {
public TestStoreConfig(){
super("classpath*:spring-beanannitation.xml");
}
@Test
public void myDriverManager(){
Store store = super.getBean("myDriverManager");
System.out.println(store.getClass().getName());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannitation"></context:component-scan>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:property-placeholder location="classpath:/config.properties"/>
</beans>
jdbc.username=root
password=root
url=127.0.0.1
4-7 Spring Bean装配之基于Java的容器注解说明——@Bean和@Scope
- 默认@Bean是单例的
@Configuration
public class MyConfiguration{
@Bean
@Scope("prototype")
public Encryptor encryptor(){
//...
}
}
@Bean
@Scope(value="session",proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences(){
return new UserPreferences();
}
@Bean
public Service userService(){
UserService service = new SimpleUserService();
service.setUserPreferences(userPreferences());
return service;
}
- 例子
public interface Store {
}
public class StringStore implements Store{
}
@Configuration
public class StoreConfig {
@Bean
@Scope("prototype")
public Store getStringStore(){
return new StringStore();
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestStoreConfig extends UnitTestBase {
public TestStoreConfig(){
super("classpath*:spring-beanannitation.xml");
}
@Test
public void test(){
Store store = super.getBean("getStringStore");
System.out.println(store.getClass().hashCode());
Store store1 = super.getBean("getStringStore");
System.out.println(store1.getClass().hashCode());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannitation"></context:component-scan>
</beans>
4-8 Spring Bean装配之基于Java的容器注解说明——基于泛型的自动装配
- 基于泛型的自动装配
@Configuration
public class MyConfiguration{
@Bean
public StringStore stringStore(){
return new StringStore();
}
@Bean
public IntegerStore integerStore(){
return new IntegerStore();
}
}
@Autowired
private Store<String> s1;
@Autowired
private Store<Integer> s2;
// Inject all Store beans as long as they have an <Integer> generic
// Store<String> benas will not appear in this list
@Autowired
private List<Store<Integer>> s;
- 例子
public interface Store<T> {
}
public class StringStore implements Store<String>{
}
public class IntegerStore implements Store<Integer> {
}
@Configuration
public class StoreConfig {
@Autowired
private Store<String> s1;
@Autowired
private Store<Integer> s2;
@Bean
public StringStore stringStore(){
return new StringStore();
}
@Bean
public IntegerStore integerStore(){
return new IntegerStore();
}
@Bean(name = "testStore")
public Store store(){
System.out.println(s1.getClass().getName());
System.out.println(s2.getClass().getName());
return new StringStore();
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestStoreConfig extends UnitTestBase {
public TestStoreConfig(){
super("classpath*:spring-beanannitation.xml");
}
@Test
public void test(){
super.getBean("testStore");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd" >
<context:component-scan base-package="com.imooc.beanannitation"></context:component-scan>
</beans>
Autowire扩张内容,自定义qualifier注解
- CustomAutowireConfigurer是BeanFactoryPostProcessor的子类,通过它可以注册自己的
qualifier注解类型(即使没有使用Spring的@Qualifier注解)
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifier</value>
</set>
</property>
</bean>
- 该AutowireCandidateResolver决定自动装配的候选者
- 每个bean定义的autowire-candidate值
- 任何中的default-autowire-candidates
- @Qualifier注解及使用CustomAutowireConfigure的自定义类型
4-9 Spring Bean装配之Spring对JSR支持的说明
@Resource
- Spring还支持使用JSR-250@Resource注解的变量或setter方法,
这是一种在Java EE 5和6的通用模式,Spring管理的对象也支持
这种模式 - @Resource有一个name属性,并且默认Spring解释该值作为被注入
bean的名称
public class SimpleMovieListener{
private MovieFinder movieFinder;
@Resource(name = "myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
}
- 如果没有显式地指定@Resource的name,默认的名称是从属性名或者setter方法得出
public class SimpleMovieListener{
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder){
this.movieFinder = movieFinder;
}
}
- 注解提供的名字被解析为一个bean的名称,这是由ApplicationContext的中的
CommonAnnotationBeanPostProcessor发现并处理的
@PostConstruct and @PreDestroy
- CommonAnnotationBeanPostProcessor不仅能识别JSR-250中的生命周期注解
@Resource,在Spring2.5中引入支持初始化回调和销毁回掉,前提是
CommonAnnotationBeanPostProcessor是Spring的ApplicationContext中注册的
public class CachingMovieLister{
@PostConstruct
public void populateMovieCache(){
//populates the movie cache upon initialization
}
@PreDestroy
public void clearMovieCache(){
//clears the movie cache upon destruction
}
}
- 例子
@Repository
public class JsrDAO {
public void save() {
System.out.println("JsrDAO invoked.");
}
}
//@Service
@Named
public class JsrServie {
// @Resource
// @Inject
private JsrDAO jsrDAO;
// @Resource
@Inject
public void setJsrDAO(@Named("jsrDAO") JsrDAO jsrDAO) {
this.jsrDAO = jsrDAO;
}
@PostConstruct
public void init() {
System.out.println("JsrServie init.");
}
@PreDestroy
public void destroy() {
System.out.println("JsrServie destroy.");
}
public void save() {
jsrDAO.save();
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestJsr extends UnitTestBase {
public TestJsr() {
super("classpath*:spring-beanannotation.xml");
}
@Test
public void testSave() {
JsrServie service = getBean("jsrServie");
service.save();
}
}
第5章 Spring AOP基本概念
5-1 AOP基本概念及特点
- 什么是AOP及实现方式
- AOP基本概念
- Spring中的AOP
- Schema-based AOP
- Spring AOP API
AspectJ
AOP:Aspect Oriented Programming的缩写,意为:面向切面编程,
通过预编译方式和运行期动态代理实现程序功能的统一维护的一种
技术- 主要功能是:日志记录,性能统计,安全控制,事务处理,异常处理
等等
AOP实现方式
- 预编译
- AspectJ
- 运行期间动态代理(JDK动态代理、CGLib动态代理)
- SpringAOP、JbossAOP
AOP几个关键概念
名称 | 说明 |
---|---|
切点(Aspect) | 一个关注点的模块化,这个关注点可能会横切多个对象 |
连接点(Joinpoint) | 程序执行过程中某个特定的点 |
通知(Advice) | 在切面的某个特定的连接点上执行的动作 |
切入点(Pointcut) | 匹配连接点的断言,在AOP中通知和一个切入点表达式关联 |
引入(Introduction) | 在不修改类代码的前提下,为类添加新的方法和属性 |
目标对象(Target Object) | 被一个或多个切面所通知的对象 |
AOP代理(AOP Proxy) | AOP框架创建的对象,用来实现切面契约(aspect contract)(包括通知方法执行等功能) |
织入(Weaving) | 把切面连接到其它的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入,执行时织入 |
Advice的类型
名称 | 说明 |
---|---|
前置通知(Before advcie) | 在某连接点(join point)之前执行的通知,但不能阻止连接点前的执行(除非它抛出一个异常) |
返回后通知(After returning advice) | 在某连接点(join point)正常执行完成后执行的通知 |
抛出异常后通知(After throwing advice) | 在方法抛出异常退出时执行的通知 |
后通知(After(finally)advice) | 当某个连接点退出的时候执行的通知(不论是正常返回还是异常退出) |
环绕通知(Around Advice) | 包围一个连接点(join point)的通知 |
Spring框架中AOP的用途
- 提供了声明式的企业服务,特别是EJB的替代服务声明
- 允许用户定义自己的方面,已完成OOP与AOP的互补使用
Spring的AOP实现
- 纯java实现,无需特殊的编译过程,不需要控制类加载器层次
- 目前只支持方法执行连接点(通知Spring Bean的方法执行)
- 不是为了提供最完整的AOP实现(尽管它非常强大);而是侧重
于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助
解决企业应用中的常见问题 - Spring AOP不会与AspectJ竞争,从而提供综合全面的AOP解决方案
有接口和无接口的Spring AOP实现区别
- Spring AOP默认使用标准的JavaSE动态代理作为AOP代理,这使得
任何接口(或者接口集)都可以被代理 - Spring AOP中也可以使用CGLIB代理(如果一个业务对象并没有实现
一个接口)
5-2 配置切面aspect
Schema – based AOP
Spring所有的切面和通知器都必须放在一个内(可以配置
包含多个元素),每一个可以包含pointcut,
advisor和aspect元素(它们必须按照这个顺序进行声明)
风格的配置大量使用了Spring的自动代理的机制
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
</aop:aspect>
</aop:config>
<bean id="aBean" class="...">
...
</bean>
5-3 配置切入点Pointcut
- execution(public**(..))
切入点为执行所有public方法时 - execution(set(..))
切入点为执行所有set开始的方法时 - execution(com.xyz.service.AccountService.(..))
切入点为执行AccountService类中的所有方法时 - execution(*com.xyz.service..(..))
切入点为执行com.xyz.service包下的所有方法时 - execution(*com.xyz.service…(..))
切入点为执行com.xyz.service包及其子包下的所有方法时 - within(com.xyz.service.*)(only in Spring AOP)
- within(com.xyz.service..*)(only in Spring AOP)
within用于匹配指定类型内的方法执行 this(com.xyz.service.AccountService)(only in Spring AOP)
this用于匹配当前AOP代理对象类型的执行方法target(com.xyz.service.AccountService)(only in Spring AOP)
target用于匹配当前目标对象类型的执行方法- args(java.io.Serializable)(only in Spring AOP)
args用于匹配当前执行的方法传入的参数为指定类型的执行方法 - @target(org.springframework.transaction.annotation.Transactional)(only in Spring AOP)
- @within(org.springframework.transaction.annotation.Transaction)(only in Spring AOP)
- @annotation(org.springframework.transaction.annotation.Transactional)(only in Spring AOP)
- @args(com.xyz.security.Classified)(only in Spring AOP)
- bean(tradeService)(only in Spring AOP)
- bean(*Service)(only in Spring AOP)
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service..(..))"/>
</aop:config>
<aop:config>
<aop:pointcut id="businessService"
expression="com.xyz.myapp.SystemArchitecture.businessService()"/>
</aop:config>
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service..(..))"/>
...
</aop:aspect>
</aop:config>
5-4 Advice应用(上)
- Before
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
<aop:aspect id="beforeExample" ref="aBean">
<aop:before
pointcut="execution(* com.xyz.myapp.dao..(..))"
method="doAccessCheck"/>
...
</aop:aspect>
- After returning advice
<aop:aspect id="afterReturnExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
<aop:aspect id="afterReturningExample" ref="aBean">
<aop:after-returning
pointcut-ref="dataAccessOperation"
returing="retVal"
method="doAccessCheck"/>
...
</aop:aspect>
- After throwing advice
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
- 使用throwing属性来指定可被传递的异常的参数名称
<aop:aspect id="afterThrowingExample" ref="aBean">
<aop:after-throwing
pointcut-ref="dataAccessOperation"
throwing="dataAccessEx"
method="doAccessCheck"/>
...
</aop:aspect>
- After(finally) advice
<aop:aspect id="afterFinallyExample" ref="aBean">
<aop:after
pointcut-ref="dataAccessOperation"
method="doAccessCheck"/>
...
</aop:aspect>
5-5 Advice应用(下)
- Around advice
- 通知方法的第一个参数必须是ProceedingJoinPoint类型
<aop:aspect id="aroundExample" ref="aBean">
<aop:after
pointcut-ref="businessService"
method="doBasicProfiling"/>
...
</aop:aspect>
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable{
//start stopwatch
Object retVal = pjp.proceed();
//stop stopwatch
return retVal;
}
- Advice parameters
public interface FooService{
Foo getFoo(String fooName,int age);
}
public class DefaultFooService implements FooService{
public Foo getFoo(String name,int age){
return new Foo(name,age);
}
}
public class SimpleProfiler{
public Object profile(ProceedingJoinPoint call,String name,int age) throws Throwable{
StopWatch clock = new StopWatch("Profiling for" + name + " and " + age + "");
try{
clock.start(call.toShortString());
return call.proceed();
}finally{
clock.stop();
System.out.println(clock.prettyPrint());
}
}
}
<bean id="fooservice" class="x.y.service.DefaultFooService" />
<bean id="profiler" class="x.y.SimpleProfiler" />
<aop:config>
<aop:aspect ref="profiler">
<aop:pointcut id="theExecutionOfSomeFooServiceMethod"
expression="execution(* x.y.service.FooService.getFoo(String,int))
and args(name,age)" />
<aop:around pointcut-ref="theExecutionOfSomeFooServiceMethod"
method="profile"/>
</aop:aspect>
</aop:config>
- 例子
public class AspectBiz {
public void biz() {
System.out.println("AspectBiz biz.");
// throw new RuntimeException();
}
public void init(String bizName, int times) {
System.out.println("AspectBiz init : " + bizName + " " + times);
}
}
public class MoocAspect {
public void before() {
System.out.println("MoocAspect before.");
}
public void afterReturning() {
System.out.println("MoocAspect afterReturning.");
}
public void afterThrowing() {
System.out.println("MoocAspect afterThrowing.");
}
public void after() {
System.out.println("MoocAspect after.");
}
public Object around(ProceedingJoinPoint pjp) {
Object obj = null;
try {
System.out.println("MoocAspect around 1.");
obj = pjp.proceed();
System.out.println("MoocAspect around 2.");
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
public Object aroundInit(ProceedingJoinPoint pjp, String bizName, int times) {
System.out.println(bizName + " " + times);
Object obj = null;
try {
System.out.println("MoocAspect aroundInit 1.");
obj = pjp.proceed();
System.out.println("MoocAspect aroundInit 2.");
} catch (Throwable e) {
e.printStackTrace();
}
return obj;
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPSchemaAdvice extends UnitTestBase {
public TestAOPSchemaAdvice() {
super("classpath:spring-aop-schema-advice.xml");
}
@Test
public void testBiz() {
AspectBiz biz = super.getBean("aspectBiz");
biz.biz();
}
@Test
public void testInit() {
AspectBiz biz = super.getBean("aspectBiz");
biz.init("moocService", 3);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
<bean id="moocAspect" class="com.imooc.aop.schema.advice.MoocAspect"></bean>
<bean id="aspectBiz" class="com.imooc.aop.schema.advice.biz.AspectBiz"></bean>
<aop:config>
<aop:aspect id="moocAspectAOP" ref="moocAspect">
<aop:pointcut expression="execution(* com.imooc.aop.schema.advice.biz.*Biz.*(..))" id="moocPiontcut"/>
<aop:before method="before" pointcut-ref="moocPiontcut" />
<!-- <aop:after-returning method="afterReturning" pointcut-ref="moocPiontcut"/> -->
<!-- <aop:after-throwing method="afterThrowing" pointcut-ref="moocPiontcut"/> -->
<!-- <aop:after method="after" pointcut-ref="moocPiontcut"/> -->
<!-- <aop:around method="around" pointcut-ref="moocPiontcut"/> -->
<!-- <aop:around method="aroundInit" pointcut="execution(* com.imooc.aop.schema.advice.biz.AspectBiz.init(String, int)) -->
<!-- and args(bizName, times)"/> -->
</aop:aspect>
</aop:config>
</beans>
5-6 Introductions应用
- 简介允许一个切面声明一个实现指定接口的通知对象且提供一个接口实现类来代表
这些对象 - 由中的元素声明,该元素用于声明所匹配的
类型拥有一个新的parent(因此得名)
<aop:aspect id="usageTrackerAspect" ref="usageTracking">
<aop:declare-parents
types-matching="com.xzy.myapp.service.*+"
implements-interface="com.xyz.myapp.service.tracking.UsageTracked"
default-impl="com.xyz.myapp.service.tracking.DefaultUsageTracked"/>
<aop:before
pointcut="com.xyz.myapp.SystemArchitecture.businessService()
and this(usageTracked)"
method="recordUsage"/>
</aop:aspect>
public void recordUsage(UsageTracked usageTracked){
usageTracked.incrementUseCount();
}
UsageTracked usageTracked = (UsageTracked) context.getBean("myService");
- 例子
public interface Fit {
void filter();
}
public class FitImpl implements Fit {
@Override
public void filter() {
System.out.println("FitImpl filter.");
}
}
@Test
public void testFit() {
Fit fit = (Fit)super.getBean("aspectBiz");
fit.filter();
}
<aop:declare-parents types-matching="com.imooc.aop.schema.advice.biz.*(+)"
implement-interface="com.imooc.aop.schema.advice.Fit"
default-impl="com.imooc.aop.schema.advice.FitImpl"/>
Aspect instantiation models
- schema-defaulted aspects只支持singleton models
5-7 Advisors
- advisor就像一个小的自包含的方面,只有一个advice
- 切面自身通过一个bean表示,并且必须实现advice接口,同时,
advisor也可以很好的利用AspectJ的切入点表达式 - Spring通过配置文件中元素支持advisor
实际使用中,大多数情况下它会和transaction advice配合使用 - 为了自定义一个advisor的优先级以便让advice可以有序,可以
使用order属性来定义advisor的顺序
<aop:config>
<aop:pointcut id="businessService"
expression="execution(* com.xyz.myapp.service..(..))"/>
<aop:advisor
pointcut-ref="businessService"
advice-ref="tx-advice"/>
</aop:config>
<tx:advice id="tx-advice">
<tx:attribute>
<tx:method name="*" propagation="REQUIRED"/>
</tx:attribute>
</tx:advice>
- 例子
@Service
public class InvokeService {
public void invoke() {
System.out.println("InvokeService ......");
}
public void invokeException() {
throw new PessimisticLockingFailureException("");
}
}
public class ConcurrentOperationExecutor implements Ordered {
private static final int DEFAULT_MAX_RETRIES = 2;
private int maxRetries = DEFAULT_MAX_RETRIES;
private int order = 1;
public void setMaxRetries(int maxRetries) {
this.maxRetries = maxRetries;
}
public int getOrder() {
return this.order;
}
public void setOrder(int order) {
this.order = order;
}
public Object doConcurrentOperation(ProceedingJoinPoint pjp) throws Throwable {
int numAttempts = 0;
PessimisticLockingFailureException lockFailureException;
do {
numAttempts++;
System.out.println("Try times : " + numAttempts);
try {
return pjp.proceed();
} catch (PessimisticLockingFailureException ex) {
lockFailureException = ex;
}
} while (numAttempts <= this.maxRetries);
System.out.println("Try error : " + numAttempts);
throw lockFailureException;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.imooc.aop.schema"></context:component-scan>
<aop:config>
<aop:aspect id="concurrentOperationRetry" ref="concurrentOperationExecutor">
<aop:pointcut id="idempotentOperation"
expression="execution(* com.imooc.aop.schema.advisors.service.*.*(..)) " />
<!-- expression="execution(* com.imooc.aop.schema.service.*.*(..)) and -->
<!-- @annotation(com.imooc.aop.schema.Idempotent)" /> -->
<aop:around pointcut-ref="idempotentOperation" method="doConcurrentOperation" />
</aop:aspect>
</aop:config>
<bean id="concurrentOperationExecutor" class="com.imooc.aop.schema.advisors.ConcurrentOperationExecutor">
<property name="maxRetries" value="3" />
<property name="order" value="100" />
</bean>
</beans>
第6章 Spring AOP的API介绍
6-1 Spring AOP API的Pointcut、advice概念及应用
Spring AOP API
- 这是Spring1.2历史用法,现在(V4.0)仍然支持
- 这是SpringAOP基础,不得不了解
- 现在的用法也是基于历史的,只是更简便了
Pointcut
- 实现之一:NameMatchMethodPointcut,根据方法名字进行匹配
- 成员变量:mappedNames,匹配的方法名集合
<bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMetchMethodPointcut">
<property name="mappedNames">
<list>
<value>sa*</value>
</list>
</property>
</bean>
Before advice
- 一个简单的通知类型
- 只是在进入方法之前被调用,不需要MethodInvocation对象
- 前置通知可以在连接点执行之前插入自定义行为,但不能改变返回值
void before(Method m,Object[] args,Object target) throws Throwable;
}
public class CountingBeforeAdvice implements MethodBeforeAdvice{
private int count;
public void before(Method m,Object[] args,Object target) throws Throwable{
++count;
}
public int getCount(){
return count;
}
}
Throws advice
- 如果连接点抛出异常,throws advice在连接点返回后被调用
- 如果throws-advice的方法抛出异常,那么它将覆盖原有异常
- 接口org.springframework.aop.ThrowsAdvice不包含任何方法,
仅仅是一个申明,实现类需要实现下面类似的方法: void afterThrowing([Method,args,target],ThrowableSubclass);
public void afterThrowing(Exception ex);
- public void afterThrowing(RemoteException ex);
- public void afterThrowing(Method method,Object[] args,Object target,Exception ex);
- public void afterThrowing(Methdo method,Object[] args,Object target,ServletException ex);
public static class CombinedThrowsAdvice implements ThrowsAdvice{
public void afterThrowing(RemoteException ex) throws Throwable{
//Do something with remote exception
}
public void afterThrowing(Method method, Object[] args, Object target, ServletException ex){
//Do something with all arguments
}
}
After Returning advice
- 后置通知必须实现org.springframework.aop.AfterReturningAdvcie接口
public class CountingAfterReturningAdvice implements AfterReturningAdvice{
private int count;
public void afterReturing(Object returnValue,Method m,Object[] args, Object target) throws Throwable{
++count;
}
public int getCount(){
return count;
}
}
- 可以访问返回值(但不能进行修改)、被调用的方法、方法的参数和目标
- 如果抛出异常,将会抛出拦截器链,替代返回值
Interception around advcie
- Spring的切入点模型使得切入点可以独立于advice重用,以针对不同的
advice可以使用想同的切入点
public interface MethodInterceptor extends Interceptor{
Object invoke(MethodInvocation invocation) throws Throwable;
}
public class DebugInterceptor implements MethodInterceptor{
public Object invoke(MethodInvocation invocation) throws Trowable{
System.out.println("Before: invocation=["+ invocation + "]");
Object rval = invocation.proceed();
System.out.println("Invocation returned");
return rval;
}
}
Introduction advice
- Spring把引入通知作为一种特殊的拦截器
- 需要IntroductionAdvisor和IntroductionInterceptor
- 仅适用于类,不能和任何切入点一起使用
public interface IntroductionInterceptor extends MethodInterceptor{
boolean implementsInterface(Class intf);
}
public interface IntroductionAdvisor extends Advisor,IntroductionInfo{
ClassFilter getClassFilter();
void validateInterfaces() throws IllegalArgumentException;
}
public interface IntroductionInfo{
Class[] getInterfaces();
}
- 一个Spring test suite的例子
- 如果调用lock()方法,希望所有的setter方法抛出LockedException异常(如果物体不可变,AOP典型例子)
- 需要一个完成繁重任务的IntroductionInterceptor,这种情况下,可以使用org.springframework.aop.support.DelegatingIntroductionInterceptor
public interface Lockable{
void lock();
void unlock();
boolean locked();
}
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable{
private boolean locked;
public void lock(){
this.locked = true;
}
public void unlock(){
this.locked = false;
}
public boolean locked(){
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if(locked() && invocation.getMethod().getName().indexOf("set") == 0){
throw new LockedException();
}
return super.invoke(invocation);
}
}
- introduction advisor比较简单,持有独立的LockMixin实例
public class LockMixinAdvisor extends DefaultIntroductionAdvisor{
public LockMixinAdvisor(){
super(new LockMixin(),Lockable.class);
}
}
Advisor API in Spring
- Advisor是仅包含一个切入点表达式关联的单个通知的方面
- 除了introductions,advisor可用于任何通知
- org.springframework.aop.support.DefaultPointcutAdvisor是最常用的advisor类,
它可以与MethodInterceptor,BeforeAdvice或者ThrowsAdvice一起使用 - 它可以混合在Spring同一个AOP代理的advisor和advice
6-2 ProxyFactoryBean及相关内容(上)
- 创建Spring AOP代理的基本方法是使用
org.springframework.aop.framework.ProxyFactoryBean - 这可以完全控制切入点和通知(advice)以及他们的顺序
foo–>ProxyFactoryBean实现里getObject()方法创建的对象
getObject方法将创建一个AOP代理包装一个目标对象
- 使用ProxyFactoryBean或者其它IoC相关类来创建AOP代理的最重要好处是通知和切入点
也可以由IoC来管理 - 被代理类没有实现任何接口,使用CGLIB代理,否者JDK代理
- 通过设置proxyTargetClass为true,可以强制使用CGLIB
- 如果目标类实现一个(或多个)接口,那么创建代理的类型将依赖ProxyFactoryBean的配置
- 如果ProxyFactoryBean的proxyInterfaces属性被设置为一个或者多个全限定接口名,基于
JDK的代理将被创建 - 如果ProxyFactoryBean的proxyInterfaces属性没有被设置,但是目标类实现了一个(或者
更多)接口,那么ProxyFactoryBean将自动检测到这个目标类已经实现了至少一个接口,
创建一个基于JDK的代理。
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty" value="Custom string property value"/>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
</bean>
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person" />
<property name="target" ref="personTarget" />
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
<bean id="personUser" class="com.mycompany.PersonUser">
<property name="person"><ref bean="person"/></property>
</bean>
- 可以使用匿名内部bean来隐藏目标和代理之间的区别
<bean id="personTarget" class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
<bean id="myAdvisor" class="com.mycompany.MyAdvisor">
<property name="someProperty" value="Custom string property value"/>
</bean>
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor">
</bean>
<bean id="person" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces" value="com.mycompany.Person" />
<property name="target">
<bean class="com.mycompany.PersonImpl">
<property name="name" value="Tony"/>
<property name="age" value="51"/>
</bean>
</property>
<property name="interceptorNames">
<list>
<value>myAdvisor</value>
<value>debugInterceptor</value>
</list>
</property>
</bean>
6-3 ProxyFactoryBean及相关内容(下)
Proxying classes
- 前面的例子如果没有使用Person接口,这种情况下Spring会使用CGLIB代理,
而不是JDK动态代理 - 如果想,可以在任何情况下使用CGLIB,即使有接口
- CGLIB代理的工作原理是在运行时生成目标类的子类,Spring配置这个生成的
子类委托方法调用到原来的目标 子类是用来实现Decorator模式,织入通知
CGLIB的代理对用户是透明的,需要注意:
- final方法不能被通知,因为它们不能被覆盖
- 不用把CGLIB添加到classpath中,在Spring3.2中,CGLIB被重新包装并包含在
Spring核心的JAR(即基于CGLIB的AOP就像JDK动态代理一样“开箱即用”)
使用global advisors
- 用*做通配,匹配所有拦截器加入通知链
<bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="service"/>
<property name="interceptorNames">
<list>
<value>global*</value>
</list>
</property>
</bean>
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor" />
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor" />
- 用*做通配,匹配所有拦截器加入通知链
<bean id="proxy" class="org.springframework.ProxyFactoryBean">
<property name="target" ref="service" />
<property name="interceptorNames">
<list>
<value>global*</value>
</list>
</property>
</bean>
<bean id="global_debug" class="org.springframework.aop.interceptor.DebugInterceptor"/>
<bean id="global_performance" class="org.springframework.aop.interceptor.PerformanceMonitorInterceptor"/>
简化的proxy定义
- 使用父子bean定义,以及内部bean定义,可能会带来更清洁和更简洁的代理定义
(抽象属性标记父bean定义为抽象的这样它不能被实例化)
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<bean id="myService" parents="txProxyTemplate">
<property name="target" >
<bean class="org.springframework.samples.MyServiceImpl"></bean>
</property>
</bean>
<bean id="mySpecialService" parent="txProxyTemplate">
<property name="target">
<bean class="org.springframework.samples.MySpecialServiceImpl">
</bean>
</property>
<property name="transactionAttributes">
<props>
<prop key="get*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="find*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="load*">PROPAGATION_REQUIRED,readOnly</prop>
<prop key="store*">PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
使用ProxyFactory
- 使用Spring AOP而不必依赖于Spring IoC
ProxyFactory factory = new ProxyFactory(myBusinessInterfaceImpl);
factory.addAdvice(myMethodInterceptor);
factory.addAdvisor(myAdvisor);
MyBusinessInterface tb = (MyBusinessInterface)factory.getProxy();
- 大多数情况下最佳实践是IoC容器创建AOP代理
- 虽然可以编码方式实现,但是Spring推荐使用配置或注解方式实现
使用”auto-proxy“
- Spring也允许使用”自动代理“的bean定义,它可以自动代理选定的bean,
这是建立在Spring的”bean post processor“功能基础上的(在加载bean
的时候就可以修改) - BeanNameAutoProxyCreator
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames" value="jdk*,onlyJdk" />
<property name="interceptorNames">
<list>
<value>myInterceptor</value>
</list>
</property>
</bean>
- DefaultAdvisorAutoProxyCreator,当前IoC容器中自动应用,不用显示声明引用advisor的bean
定义
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdisorAutoProxyCreator" />
<bean class="org.springframework.transaction.interceptor.TransactionAttributeSourceAdvisor">
<property name="transacationInterceptor" ref="transactionInterceptor"/>
</bean>
<bean id="customAdvisor" class="com.mycompany.MyAdvisor" />
<bean id="businessObject1" class="com.mycompany.BusinessObject1">
<!-- Properties omitted -->
</bean>
<bean id="businessObject2" class="com.mycompany.BusinessObject2"/>
- 例子
public interface BizLogic {
String save();
}
public class BizLogicImpl implements BizLogic {
public String save() {
System.out.println("BizLogicImpl : BizLogicImpl save.");
return "BizLogicImpl save.";
// throw new RuntimeException();
}
}
public class MoocAfterReturningAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable {
System.out.println("MoocAfterReturningAdvice : " + method.getName() + " " +
target.getClass().getName() + " " + returnValue);
}
}
public class MoocBeforeAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("MoocBeforeAdvice : " + method.getName() + " " +
target.getClass().getName());
}
}
public class MoocMethodInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("MoocMethodInterceptor 1 : " + invocation.getMethod().getName() + " " +
invocation.getStaticPart().getClass().getName());
Object obj = invocation.proceed();
System.out.println("MoocMethodInterceptor 2 : " + obj);
return obj;
}
}
public class MoocThrowsAdvice implements ThrowsAdvice {
public void afterThrowing(Exception ex) throws Throwable {
System.out.println("MoocThrowsAdvice afterThrowing 1");
}
public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable {
System.out.println("MoocThrowsAdvice afterThrowing 2 : " + method.getName() + " " +
target.getClass().getName());
}
}
public interface Lockable {
void lock();
void unlock();
boolean locked();
}
public class LockMixin extends DelegatingIntroductionInterceptor implements Lockable {
private static final long serialVersionUID = 6943163819932660450L;
private boolean locked;
public void lock() {
this.locked = true;
}
public void unlock() {
this.locked = false;
}
public boolean locked() {
return this.locked;
}
public Object invoke(MethodInvocation invocation) throws Throwable {
if (locked() && invocation.getMethod().getName().indexOf("set") == 0) {
throw new RuntimeException();
}
return super.invoke(invocation);
}
}
public class LockMixinAdvisor extends DefaultIntroductionAdvisor {
private static final long serialVersionUID = -171332350782163120L;
public LockMixinAdvisor() {
super(new LockMixin(), Lockable.class);
}
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAOPAPI extends UnitTestBase {
public TestAOPAPI() {
super("classpath:spring-aop-api.xml");
}
@Test
public void testSave() {
BizLogic logic = (BizLogic)super.getBean("bizLogicImpl");
logic.save();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="moocBeforeAdvice" class="com.imooc.aop.api.MoocBeforeAdvice"></bean>
<bean id="moocAfterReturningAdvice" class="com.imooc.aop.api.MoocAfterReturningAdvice"></bean>
<bean id="moocMethodInterceptor" class="com.imooc.aop.api.MoocMethodInterceptor"></bean>
<bean id="moocThrowsAdvice" class="com.imooc.aop.api.MoocThrowsAdvice"></bean>
<!-- <bean id="bizLogicImplTarget" class="com.imooc.aop.api.BizLogicImpl"></bean> -->
<!-- <bean id="pointcutBean" class="org.springframework.aop.support.NameMatchMethodPointcut"> -->
<!-- <property name="mappedNames"> -->
<!-- <list> -->
<!-- <value>sa*</value> -->
<!-- </list> -->
<!-- </property> -->
<!-- </bean> -->
<!-- <bean id="defaultAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"> -->
<!-- <property name="advice" ref="moocBeforeAdvice" /> -->
<!-- <property name="pointcut" ref="pointcutBean" /> -->
<!-- </bean> -->
<!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> -->
<!-- <property name="target"> -->
<!-- <ref bean="bizLogicImplTarget"/> -->
<!-- </property> -->
<!-- <property name="interceptorNames"> -->
<!-- <list> -->
<!-- <value>defaultAdvisor</value> -->
<!-- <value>moocAfterReturningAdvice</value> -->
<!-- <value>moocMethodInterceptor</value> -->
<!-- <value>moocThrowsAdvice</value> -->
<!-- </list> -->
<!-- </property> -->
<!-- </bean> -->
<!-- <bean id="bizLogicImplTarget" class="com.imooc.aop.api.BizLogicImpl"></bean> -->
<!-- <bean id="bizLogicImpl" class="org.springframework.aop.framework.ProxyFactoryBean"> -->
<!-- <property name="proxyInterfaces"> -->
<!-- <value>com.imooc.aop.api.BizLogic</value> -->
<!-- </property> -->
<!-- <property name="target"> -->
<!-- <bean class="com.imooc.aop.api.BizLogicImpl"></bean> -->
<!-- <ref bean="bizLogicImplTarget"/> -->
<!-- </property> -->
<!-- <property name="interceptorNames"> -->
<!-- <list> -->
<!-- <value>moocBeforeAdvice</value> -->
<!-- <value>moocAfterReturningAdvice</value> -->
<!-- <value>moocMethodInterceptor</value> -->
<!-- <value>moocThrowsAdvice</value> -->
<!-- <value>mooc*</value> -->
<!-- </list> -->
<!-- </property> -->
<!-- </bean> -->
<bean id="baseProxyBean" class="org.springframework.aop.framework.ProxyFactoryBean"
lazy-init="true" abstract="true"></bean>
<bean id="bizLogicImpl" parent="baseProxyBean">
<property name="target">
<bean class="com.imooc.aop.api.BizLogicImpl"></bean>
</property>
<property name="proxyInterfaces">
<value>com.imooc.aop.api.BizLogic</value>
</property>
<property name="interceptorNames">
<list>
<value>moocBeforeAdvice</value>
<value>moocAfterReturningAdvice</value>
<value>moocMethodInterceptor</value>
<value>moocThrowsAdvice</value>
</list>
</property>
</bean>
</beans>
第7章 Spring对AspectJ的支持
7-1 AspectJ介绍及Pointcut注解应用
AspectJ
- @AspectJ的风格类似纯java注解的普通java类
- Spring可以使用AspectJ来做切入点解析
AOP的运行时仍旧是纯的Spring AOP,对AspectJ的编译器或者织入无依赖性
对@AspectJ支持可以使用XML或者Java风格的配置
- 确保AspectJ的aspectjweaver.jar库包含在应用程序(版本1.6.8)的classpath中
@Configuration
@EnableAspectJAutoProxy
public class AppConfig{
}
<aop:aspectj-autoproxy/>
- @AspectJ切面使用@Aspect注解配置,拥有@Aspect的任何bean将被Spring自动识别并应用
- 用@Aspect注解的类可以有方法和字段,他们也可能包括切入点(pointcut),通知(Advice)
和引入(introduction)声明 - @Aspect注解是不能够通过类路径自动检测发现的,所以需要配合使用@Component注释或者
在xml配置bean - 一个类中的@Aspect注解标识它为一个切面,并且将自己从自动代理中排除
<bean id="myAspect" class="org.xyz.NotVeryUsefulAspect">
</bean>
package org.xyz;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class NotVeryUsefulAspect{
}
pointcut
- 一个切入点通过一个普通的方法定义来提供,并且切入点表达式使用@Pointcut注解,
方法返回类型必须为void - 定义一个名为‘anyOldTransfer’,这个切点将匹配任何名为”transfer“的方法执行
@Pointcut("execution(* transfer(..))")//the pointcut expression
private void anyOldTransfer(){}//the pointcut signature
Supported Pointcut Designators
execution |
within |
this |
target |
args |
@target |
@args |
@within |
@annotation |
组合pointcut
- 切入点表达式可以通过&&、||和!进行组合,也可以通过名字引入切入点表达式
- 通过组合,可以建立更加复杂的切入点表达式
@Pointcut("execution(public * (..))")
private void anyPublicOperation(){}
@Pointcut("within(com.xyz.someapp.trading...)")
private void inTrading(){}
@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation(){}
定义良好的pointcuts
- AspectJ是编译期的AOP
- 检查代码并匹配连接点与切入点的代价是昂贵的
- 一个好的切入点应该包括以下几点
- 选择特定类型的连接点,如:execution,get,set,call,handler
- 确定连接点范围,如:within,withincode
- 匹配上下文信息,如:this,target,@annotation
7-2 Advice定义及实例
Before advice
@Componet
@Aspect
public class MoocAspect{
@Before("execution(* com.imooc.aop.aspect.biz.*Biz.*(..))")
public void before(){
//...
}
}
在执行com.imooc.aop.aspectj包下以Biz结尾的类的所有方法时匹配Advice
After returning advice
@Aspect
public class AfterReturningExample{
@AfterReturning("com.xyz.myapp.SystemArchitecture.dataAccessOperation()")
public void doAccessCheck(){
//...
}
}
- 有时候需要在通知体内得到返回的实际值,可以使用@AfterReturning绑定返回值的形式
@Aspect
public class AfterReturningExample{
@AfterReturning(pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",
returning="retVal")
public void doAccessCheck(Object retVal){
//...
}
}
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() throws Throwable{
//start stopwatch
Object retVal = pjp.proceed();
//stop stopwatch
return retVal;
}
}
7-3 Advice扩展
给advice传递参数
@Before("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
public void validateAccount(Account account){
//...
}
@Pointcut("com.xyz.myapp.SystemArchitecture.dataAccessOperation() && args(account,..)")
private void accountDataAccessOperation(Account account){}
@Before("accountDataAccessOperation(account)")
public void validateAccount(Account account){
//...
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Auditable{
AuditCode value();
}
@Before("com.xyz.lib.Pointcuts.anyPublicMethod() && @annotation(auditable)")
public void audit(Auditable auditable){
//...
}
Advice的参数及泛型
- Spring AOP可以处理泛型类的声明和使用方法
public interface Sample<T> {
void sampleGenericMethod(T param);
void sampleGenericCollectionMethod(Collection<T> param);
}
@Before("execution(* ..Sample+.sampleGenericMethod(*)) && args(param)")
public void beforeSampleMethod(MyType param){
//Advice implementation
}
@Before("execution(* ..Sample+.sampleGenericCollectionMethod(*)) && args(param)")
public void beforeSampleMethod(Collection<T> param){
// Advice implementation
}
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();
//... use code and bean
}
- 如果第一个参数是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();
//... use code , bean and jp
}
Introductions
- 允许一个切面声明一个通知对象实现指定接口,并且提供一个接口实现类来代表这些对象
introduction使用@DeclareParents进行注解,这个注解用来定义匹配的类型拥有一个新的parent
例如:给定一个接口UsageTracked,并且该接口拥有DefaultUsageTracked的实现,接下来的切面声明了
所有的service接口的实现都实现了UsageTracked接口
@Aspect
public class UsageTracking{
@DeclareParents(value="com.xzy.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(){
//...
}
}
- 例子
@Service
public class MoocBiz {
@MoocMethod("MoocBiz save with MoocMethod.")
public String save(String arg) {
System.out.println("MoocBiz save : " + arg);
// throw new RuntimeException(" Save failed!");
return " Save success!";
}
}
@Component
@Aspect
public class MoocAspect {
@Pointcut("execution(* com.imooc.aop.aspectj.biz.*Biz.*(..))")
public void pointcut() {}
@Pointcut("within(com.imooc.aop.aspectj.biz.*)")
public void bizPointcut() {}
@Before("pointcut()")
public void before() {
System.out.println("Before.");
}
@Before("pointcut() && args(arg)")
public void beforeWithParam(String arg) {
System.out.println("BeforeWithParam." + arg);
}
@Before("pointcut() && @annotation(moocMethod)")
public void beforeWithAnnotaion(MoocMethod moocMethod) {
System.out.println("BeforeWithAnnotation." + moocMethod.value());
}
@AfterReturning(pointcut="bizPointcut()", returning="returnValue")
public void afterReturning(Object returnValue) {
System.out.println("AfterReturning : " + returnValue);
}
@AfterThrowing(pointcut="pointcut()", throwing="e")
public void afterThrowing(RuntimeException e) {
System.out.println("AfterThrowing : " + e.getMessage());
}
@After("pointcut()")
public void after() {
System.out.println("After.");
}
@Around("pointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Around 1.");
Object obj = pjp.proceed();
System.out.println("Around 2.");
System.out.println("Around : " + obj);
return obj;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MoocMethod {
String value();
}
@RunWith(BlockJUnit4ClassRunner.class)
public class TestAspectJ extends UnitTestBase {
public TestAspectJ() {
super("classpath:spring-aop-aspectj.xml");
}
@Test
public void test() {
MoocBiz biz = getBean("moocBiz");
biz.save("This is test.");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.imooc.aop.aspectj"/>
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>