5-3 配置切入点Pointcut
配置切入点Pointcut(找到某个功能模块的具体方法)
execution用于匹配某个功能模块的具体方法。

以下SpringAOP和Spring支持的AspectJ都支持。
图一的1:执行所有的public方法时,进行切入(执行相应切面的相应功能)。
图一的2:执行所有的set方法时,进行切入。
图一的3:执行com.xy.service.AccountService类下的所有方法时,进行切入。
图一的4:执行com.xyz.service包下的所有方法时,进行切入。
图一的5:执行com.xyz.service包下以及子包下的所有方法时,进行切入。

以下为SpringAOP自己支持的。

SpringAOP其他的匹配。
图一的1:匹配目标对象。
图一的2:匹配参数的。
图一的3:按注解匹配的。
图一的4:按注解匹配的。
图一的5:按注解匹配的。
图二的1:参数里采用了何种注解。
图二的2:采用了何种bean。
图二的3:bean是以何种形式进行结尾的。(这些可以去Spring官方文档和搜索引擎搜索,会有很全面的解释,这些切入点会有很多类型,在具体使用时,可以查找相应文档)


完整的AOP配置(根据不同业务需求,查找相关文档,进行不同的expression表达式配置)
案例1:表示执行service包下的所有类的任何类型的方法时,进行切入。

案例2:表示执行SystemArchitecture的businessService()方法时,才进行切入。

案例3:执行AspectBiz中的所有方法,都会进行切入。

配置以某个单词结尾的类:例如上面Aspect编写成*。
5-2 配置切面aspect
基于Schema-based配置的AOP实现
Spring所有的切面和通知器都必须放在<aop:config>内(可以配置多个<aop:config>元素),每一个<aop:config>可以包含aspect,pointcout和advisor元素(它们必须按照这个顺序进行声明)。
<aop:config>的配置大量使用了Spring的自动代理机制。
该配置的含义:一个类作为切面来声明,切面Id是myAspect,也就是说程序执行某个功能模块时,通过pointcut和Joinpoint执行该切面的功能。
案例:两个类,一个类作为切面类,另一个作为业务类。(注意:<beans>中添加约束,xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
)
代码:
<?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-4.0.xsd">
<bean id="aspectImooc" class="aspect.AspectImooc"></bean>
<bean id="ServiceImooc" class="aspect.ServiceImooc"></bean>
<aop:config>
<aop:aspect id="aspectImoocAop" ref="aspectImooc"></aop:aspect>
</aop:config>
5-1 AOP基本概念及特点
AOP基本概念及特点
一、什么是AOP及实现方式
AOP:Aspect Oriented Programming,面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。
主要功能:日志记录,性能统计,安全控制,事物处理,异常处理等等。(事务处理:任何操作数据库的方法,尤其是增删改,需要事物的处理,使用AOP时,执行到某种类型的方法,或者某一层的类,自动开启事物和获取连接、提交事物、关闭事物的步骤,简化了操作)
AOP实现方式:1——预编译方式。(AspectJ)
2——运行期动态代理。(JDK动态代理、CGLib动态代理)
(SpringAOP、JbossAOP)
切面的理解:例如系统中有产品管理、订单管理、服务管理等等模块(子功能),任一模块(子功能),都需要记录它的日志,控制它的事物,以及做一些安全验证的功能。但是如果在每一个模块(子功能)中去设计日志、事物、安全验证,会带来大量工作量,尤其当项目达到一定规模时,比如修改日志记录的方式,那么则需要修改每一个模块的日志功能,通过切面方式对开发人员是不可见的,当执行任一模块时,通过预编译或者运行期动态代理方式,都会去记录它的日志,实现了一处写功能,处处可实现的方式,对于开发人员允许不知道有这样功能的存在,这样就简化了操作(修改日志、编写事物等),业务功能横向走,切面垂直业务功能。
二、AOP基本概念(切面—>织入—>目标对象—>切入点—>连接点—>通知—>引入—>AOP代理:“切面”要执行的动作,通过“织入”与所有功能模块进行联系,又通过“目标对象”找到具体功能模块,通过“切入点”将要查找某个功能的某个方法,通过“连接点”找到该功能的特定方法的开始,通过“通知”将要执行何种切面的动作,通过“引入”引入该动作用到的对象和方法,通过“AOP代理”实现该方法。)
AOP相关概念:
1、切面(Aspect):一个关注点(事物),这个关注点可能会横切多个对象(产品管理、订单管理)。
2、连接点(Joinpoint):程序执行过程中的某个特定的点。(一个类中执行的某个方法的开始)。
3、通知(Advice):在切面的某个特定的连接点上执行的动作(方法执行的时候,切面额外执行的动作,比如说方法执行时,开启事物提交功能)。
4、切入点(Pointcut):匹配连接点的断言,在AOP中通知和一个切入点表达式关联(在切面中匹配一个具体的连接点(某个功能的方法的开始))。
5、引入(Introduction):在不修改类代码的前提下,修改的是字节码文件,为类添加新的方法和属性。(类似于编译期动态的修改class文件去增加新的属性和方法,源代码中并没有这样的属性和方法),取决于使用AspectJ和SpringAOP的实现方式,使用方式不同,为类添加属性和方法的方式是有区别的。
6、目标对象(Target Object):被一个或者多个切面所通知的对象。(例如存在一个订单的service(对象)和一个商品的service(对象),执行该模块的功能时,切面会通知相对应的service,在执行数据库操作时,去加上事物的控制,这两个service就是目标对象)。
7、AOP代理(AOP Proxy):AOP框架创建的对象,用来实现切面契约(aspect contract),包括通知方法执行等功能(这里执行的方法指的是切面里的执行相应操作所用到的方法)开发时不知道这个对象存在的,也不清楚会创建成什么样子。
8、织入(Weaving):把切面连接到其他的应用程序类型或者对象上,并创建一个被通知的对象,分为:编译时织入、类加载时织入、执行时织入。(使切面和对象(模块功能)牵连起来)
Spring官方文档给出的解释:

Advice(通知)的类型(在切面某个特定连接点执行的动作)
前置通知(Before advice):在某连接点(join point)(某个功能方法开始执行前)之前执行的通知,但不能阻止连接点前的执行(除该方法外的其他方法的执行)(除非它抛出一个异常)。
返回后通知(After returning advice):在某连接点(方法)正常完成后执行的通知(将要执行的切面功能)。
抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知(将要执行切面的功能)。
后通知(After(finally)advice):当某连接点(方法)退出的时候执行的通知(切面将要执行的功能)(不论是正常返回还是异常退出都会执行的通知)。
环绕通知(Around Advice):包围一个连接点(join point)的通知(在整个方法的内部都有切面要执行的功能存在,不分前后)。

Spring框架中AOP的用途:
用途1:提供了声明式的企业服务(也可以是其他服务,例如互联网服务),特别是EJB(重量级框架)的替代服务的声明。
用途2:允许用户定制自己的方面(切面),以完成OOP(面向对象编程,模拟世界中行为和方式,可以理解为实现一个功能的顺序)与AOP(横切的方式,可以理解为各个功能之间横切的一种功能)的互补使用,可以实现自己横切的功能。
三、Spring中的AOP(Spring中AOP的实现方式以及特点)
特点1:纯java实现,无需特殊的编译过程(普通代码需要加载才能执行),不需要控制类加载器层次。可以更好的进行控制(使用类加载器应当谨慎,容易方法生ClassNotFound异常这种情况)。
特点2:目前只支持方法执行连接点(通知Spring Bean的方法执行),(在执行某方法时,才去执行切面的功能)。
特点3:SpringAOP不是为了提供最完整的AOP实现(尽管它非常强大);而是侧重于提供一种AOP实现和Spring IoC容器之间的整合,用于帮助解决企业应用中的常见问题。
特点4:SpringAOP不会与AspectJ(完整的、全面的AOP解决方案)竞争,也不会提供综合全面的AOP解决方案。
有接口和无接口的SpringAOP实现区别:(一个类是否实现了某个接口,例如一个ProductService接口,一个ProductServiceImpl实现了该接口,在这类上切入的切面就是有接口的AOP)
SpringAOP对于有接口的使用标准的JavaSE动态代理作为AOP代理,这使得任何接口(或者接口集)都可以被代理。
SpringAOP对于没有接口的实现类中使用CGLIB代理(如果一个业务对象并没有实现一个接口)
四、Schema-based 实现AOP(基于配置文件实现AOP)
五、API实现AOP(基于API实现AOP)
六、AspectJ实现AOP
4-1 Spring Bean装配之Bean的定义及作用域的注解实现
Bean管理的注解实现及例子
1、Classpath扫描与组件管理
定义:从Spring3.0开始,Spring JavaConfig项目提供很多特性,包括通过注解定义Bean,而不是使用XML。
@Component:通用型注解,可用于任何Bean。
@Repository:通常用于注解DAO类,即持久类。
@Service:通常用于注解Service类,即服务层。
@Controller:通常用于注解Controller,即控制层。
2、类的自动检测与注册Bean
定义:Spring可以检测到类上和属性上以及方法上的注解,可以通过注解进行实例化或注入。
3、<context:annotation-config/>
定义:基于XML配置,如果使用该标签,则在描述中添加约束。

<context:component-scan>:一般处理类上的注解,它的功能包含<context:annotation-config>的功能,比较常用。
<context:annotation-config>:一般处理实例化Bean的属性和方法的注解。
SpringXML配置过滤器作用:扫描特定包下的特定注解。

4、@Component,@Repository,@Service,@Controller
5、@Required
6、@Autowired
7、@Qualifier
8、@Resource
4-4 Spring Bean装配之Autowired注解说明-3
@Qualifier:一般和@Autowired一起使用,按类型自动注入时可能会有多个Bean实例的情况,可以使用@Qualifier注解缩小范围(或指定唯一),也可用于指定单独的构造器或方法参数。
代码:
@Component(value="invoker")
public class InterfaceInvoker {
private List<InjectInterface> list;
@Autowired()
@Qualifier(value="one")
public void setList(List<InjectInterface> list) {
this.list = list;
}
public void say(){
if(null!=list){
for (int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
}else{
System.out.println("list为null");
}
}
}
@Component(value="one")
public class InterfaceImplOne implements InjectInterface {
}
@Component
public class InterfaceImplTwo implements InjectInterface {
}
结果:
injectList.InterfaceImplOne@54266750
xml文件中实现@Qualifier功能(不通过@Component)





4-3 Spring Bean装配之Autowired注解说明-2
一、可以使用@Autowired注解注入Spring提供的解析依赖性接口(ClassPathXmlApplicationContext、AnnotationApplicationContext、BeanFactory、Envieonment、ResourceLoader、ApplicatonEventPublisher、MessageSource)
二、可以对数组(集合)类型的属性进行注入,注入的是ApplicationContext中所有符合该类型或者是该类型的子类的(List<String>,则会把所有String类型的Bean注入List集合中)。


Key:Bean的Id。
Value:Bean的对象。
如果希望数组有序
1、Bean实现org.springframework.core.Ordered接口
2、@Order注解(只针对List)
InterfaceImplTwo添加@Order(value=1)
InterfaceImplOne添加@Order(value=2),先取出Two的实例,再取出One的实例。
@Autowired是由Spring BeanPostProcessor处理的,所以不能在配置类中使用它,也就是说要注入集合类型的属性,这些属性的值只能是通过xml或者在配置类中使用@Bean注解加载。
注入List<BeanInterface>代码:
public interface InjectInterface {
}
@Component
public class InterfaceImplOne implements InjectInterface {
}
@Component
public class InterfaceImplTwo implements InjectInterface {
}
@Configuration
@ComponentScan("injectList")
public class InterfaceConfig {
}
@Component(value="invoker")
public class InterfaceInvoker {
private List<InjectInterface> list;
@Autowired
public void setList(List<InjectInterface> list) {
this.list = list;
}
public void say(){
if(null!=list){
for (int i=0;i<list.size();i++) {
System.out.println(list.get(i));
}
}else{
System.out.println("list为null");
}
}
}
@Test
public void test(){
ApplicationContext ac=new AnnotationConfigApplicationContext(InterfaceConfig.class);
InterfaceInvoker ii=ac.getBean("invoker",InterfaceInvoker.class);
ii.say();
}
结果:
injectList.InterfaceImplOne@39f46204
injectList.InterfaceImplTwo@5b4f1255
注入Map类型<String,InjectInterface>类型属性(和List相似)
代码:
@Component(value="invoker")
public class InterfaceInvoker {
private Map<String,InjectInterface> map;
@Autowired
public void setMap(Map<String, InjectInterface> map) {
this.map = map;
}
public void say(){
if(map!=null&&map.size()>0){
for (Entry<String,InjectInterface> ii: map.entrySet()) {
System.out.println(ii.getKey()+" "+ii.getValue());
}
}
}
}
4-2 Spring Bean装配之Autowired注解说明-1
Spring之@Autowired注解
@Required:适用于Bean属性的set方法上,bean属性必须在配置时被填充,通过bean定义或自动装配一个明确的属性值。(不常用)

@Autowired:自动注入,一般标识在构造器、set方法、成员变量上。如果找不到注入的实例,则抛出异常,可以通过required=true属性来避免。(如果使用required属性,则使用成员变量时应进行判断是否为空)
注意:每个类的构造器都可以使用@Autowired注解,但只能有一个构造器被标记为Autowired(required=true),required默认为false,这种情况下(解决多个构造器不能使用required属性),@Autowired的必要属性,建议使用@Required注解来代替。
代码:
public interface InjectDao {
public void save(String ss);
}
public interface InjectService {
public void say(String s);
}
@Repository(value="dao")
public class InjectDaoImpl implements InjectDao {
@Override
public void save(String ss) {
System.out.println("dao保存了"+ss+":"+hashCode());
}
}
@Service(value="service")
public class InjectServiceImpl implements InjectService {
@Autowired
private InjectDaoImpl idi;
@Override
public void say(String s) {
System.out.println("service接受了参数"+s);
idi.save(s);
}
}
@Configuration
@ComponentScan("Bean")
public class StoreConfig {
}
测试:
@Test
public void test(){
ApplicationContext ac=new AnnotationConfigApplicationContext(StoreConfig.class);
InjectServiceImpl isi=ac.getBean("service",InjectServiceImpl.class);
isi.say("今天下雨了");
}
结果:
service接受了参数今天下雨了
dao保存了今天下雨了:235283278
构造器注入:
@Autowired
public InjectServiceImpl(InjectDaoImpl idi) {
super();
this.idi = idi;
}
set注入:
@Autowired
public InjectServiceImpl(InjectDaoImpl idi) {
super();
this.idi = idi;
}
@Scope和@Bean
@Bean:默认是单例模式。
@Scope:value属性指定Bean的作用域范围,proxyMode属性指定使用的代理方式(包括接口的代理和类的代理)。
代理方式主要有两种:针对接口的代理、针对类的代理,实现方式有所区别。前者是jdk动态代理,后者是cglib代理。
proxyMode:容器启动时bean还没创建 通过cglib代理这个接口或者类注入到其它需要这个bean的bean中
Java容器注解@ImportResource和@Value
一、通过xml文件进行配置。
<context:property-placeholder location="资源文件的存放位置"/>:<beans>进行配置,作用是加载资源文件(properties——Key-Value类型资源文件),然后就可以通过${Key},引用资源文件中的内容(如数据库的加载)。


代码:
public class Store {
public Store(String username,String password,String url){
System.out.println(username);
System.out.println(password);
System.out.println(url);
}
<context:property-placeholder location="classpath:/config.properties"/>
<bean id="store" class="Bean.Store">
<constructor-arg name="username" value="${jdbc.username}"></constructor-arg>
<constructor-arg name="password" value="${jdbc.password}"></constructor-arg>
<constructor-arg name="url" value="${jdbc.url}"></constructor-arg>
</bean>
测试:
@Test
public void test(){
ApplicationContext ac=new ClassPathXmlApplicationContext("spring-ioc.xml");
Store s=ac.getBean("store",Store.class);
System.out.println(s);
}
二、通过注解进行配置前提xml已经编写(配置类中添加@ImportResource("classpath:config.xml") )。
步骤1:配置类中添加@ImportResource,配置类中定好要用到的属性,并在属性上添加@Value("$(Key)")。
代码:
@Configuration
@ImportResource("classpath:/spring-ioc.xml")
public class StoreConfig {
@Value("${url}")
private String url;
@Value("${username}") //取到的是当前操作系统的用户名,所以不要使用username这个名。
private String username;
@Value("${password}")
private String password;
@Bean(name="store")
public Store getStoreBean(){
return new Store(username,password,url);
}
}
步骤二:这种注入依赖于使用@Bean标签,并返回有参构造函数对象,通过参数对Bean进行注入。

代码:xml代码:
<beans>标签里添加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" ,
<beans>中添加
<context:property-placeholder location="classpath:/config.properties"/>
java代码:
public class Store {
public Store(String username,String password,String url){
System.out.println(username);
System.out.println(password);
System.out.println(url);
}
配置文件:
username=\u5C0F\u660E
password=123456
url=localhost
4-5 Spring Bean装配之基于Java的容器注解说明——@Bean
Java的容器注解说明——@Bean
@Bean:由SpringIoc容器配置和初始化对象,类似于XML配置文件的<bean/>,。(一般@Bean 需要在配置类中使用,即类上需要加上@Configuration注解)
@Bean的name属性:可以通过name属性指定Bean的Id,相当于XML中<bean>的Id。
@Component:如果一个类上标识该注解,表示配置类可以通过@ComponentScan("路径名")扫描到该类,并实例化这个类。
举例:

@Bean的initMethod属性、destroyMethod属性:通过@Bean实例化Bean时要执行的初始化和销毁逻辑。

举报

