Spring 的IOC 和Aop
Spring 的IOC 和Aop
在ApplicationContext.xml中,bean的scope属性默认是singleton,即默认是单例
Spring容器创建的时候,会将所有的配置的bean对象创建,默认bean都是单例的,
代码通过getBean()方法从容器获取指定的bean实例,容器首先会调用Bean类的无参构造器,创建实例对象
bean的作用域
scope=“prototype” 原型模型(N个对象),真正使用时才会创建,没获取一次,都会创建不同的对象
scopr="singleton" 单例模式:容器初始化时就会初始化所有的bean对象,,每次获取的都是同一个对象,默认值
多种方式实现依赖注入
设置注入
<bean id="user3" class="cn.hmy.bean.User"> <property name="username "> <value><![CDATA[P&g]]></value> </property> </bean>
构造注入
注意:
1、一个<constructor-arg>元素表示构造方法的一个参数,且使用时不区分顺序。
2、通过<constructor-arg>元素的index 属性可以指定该参数的位置索引,位置从0 开始。
3、<constructor-arg>元素还提供了type 属性用来指定参数的类型,避免字符串和基本数据类型的混淆。
<bean id="user" class="cn.hmy.bean.User" scope="prototype"> <constructor-arg index="0" type="java.lang.String" value="lxl"/> <constructor-arg index="1" type="java.lang.String" value="lxl@163.com"/> <constructor-arg index="2" type="cn.hmy.bean.Car" ref="car"></constructor-arg> </bean>
p命名空间注入
xmlns:p="http://www.springframework.org/schema/p"
....
<!--p命名空间注入 --> <bean id="user2" class="cn.hmy.bean.User" p:username="张嘎" p:password="123"/>
c命名空间的注入
xmlns:c="http://www.springframework.org/schema/c" <!--c命名空间注入 --> <bean id="user5" class="cn.hmy.bean.User" c:car-ref="car" c:username="岁月静好" c:password="123"/>
引用其他bean组件
<!--引用其他的Bean组件 即给域属性的赋值--> <bean id="car" class="cn.hmy.bean.Car"> <property name="color" value="白色"/> </bean> <bean id="user4" class="cn.hmy.bean.User"> <property name="car" ref="car"/> </bean>
注入不同数据类型
<!-- 注入集合类型的属性 --> <!-- 注入list类型的属性 --> <bean id="collectionBean1" class="cn.hmy.bean.CollectionBean"> <property name="names"> <list> <value>张三</value> <value>李四</value> </list> </property> </bean> <!--注入set类型的属性 --> <bean id="collectionBean2" class="cn.hmy.bean.CollectionBean"> <property name="address"> <set> <value>张三2</value> <value>李四2</value> </set> </property> </bean> <!--注入map类型的属性 --> <bean id="collectionBean4" class="cn.hmy.bean.CollectionBean"> <property name="map"> <map> <entry> <key><value>football</value></key> <value>足球</value> </entry> </map> </property> </bean> <!--注入properties类型的属性 --> <bean id="collectionBean5" class="cn.hmy.bean.CollectionBean"> <property name="pro"> <props> <prop key="basketball">篮球</prop> </props> </property> </bean> <!--给数组注入值 --> <bean id="collectionBean6" class="cn.hmy.bean.CollectionBean"> <property name="strs"> <list> <value>神奇</value> <value>神奇</value> <value>神奇</value> </list> </property> </bean>
使用注解实现注入
package cn.hmy.bean; import javax.annotation.Resource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; //@Component("info") 不分层 //@Repository("info") dao //@Controller("info") action @Service("info") // biz public class User { @Value("岁月静好") //普通属性 private String username; // 用户名 @Value("123") private String age; // 密码 //@Resource 域属性 @Autowired //域属性 private Car myCar; public Car getMyCar() { return myCar; } @Override public String toString() { return "User [username=" + username + ", age=" + age + ", myCar=" + myCar + "]"; } public void setMyCar(Car myCar) { this.myCar = myCar; } public User() { System.out.println("init user"); } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getAge() { return age; } public void setAge(String age) { this.age = age; } }
<?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"> <!-- bean definitions here --> <context:component-scan base-package="cn.hmy.bean"/> </beans>
package cn.hmy.bean; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Component; @Component public class Car { @Value("白色") private String color; public String getColor() { return color; } public void setColor(String color) { this.color = color; } @Override public String toString() { return "Car [color=" + color + "]"; } }
代理
静态代理:代理模式
动态代理:JDK动态代理 和 cglib动态代理
原始对象
代理对象:对原始对象的方法做了增强
静态代理:
package cn.hmy.proxy; public interface Subject { public String request(); }
package cn.hmy.proxy; public class RealSubject implements Subject{ public String request() { return "真实对象"; } }
package cn.hmy.proxy; public class ProxySubject implements Subject{ private Subject realSubject; public Subject getRealSubject() { return realSubject; } public void setRealSubject(Subject realSubject) { this.realSubject = realSubject; } public String request() { System.out.println("代理增强"); return realSubject.request(); } }
动态代理
jdk的动态代理
本质:在内存中构建出接口的实现类
特点:被代理对象,必须有接口
package cn.hmy.dynamicproxy; public interface UserDao { public String add(); public void select(); }
package cn.hmy.dynamicproxy; public class UserDaoImpl implements UserDao{ public String add() { System.out.println("add success"); return "呵呵"; } public void select() { // TODO Auto-generated method stub } }
cglib动态代理
package cn.hmy.cglib; public class UserService { public void delete(){ System.out.println("删除成功"); } }
AOP
AOP'是一种思想,而非实现
AOP是基于OOP,而又远远高于AOP,主要是将主要核心业务和交叉业务分离,交叉业务就是切面,例如:记录日志和开启事务
AOP Aspect Oriented Programming 面向切面编程
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
面向对象编程是从【静态角度】考虑程序的结构,而面向切面编程是从【动态角度】考虑程序运行过程。
AOP底层,就是采用【动态代理】模式实现的。采用了两种代理:JDK动态代理和CGLIB动态代理。
基本术语(一些名词):
(1)切面(Aspect)
切面泛指[*交叉业务逻辑*]。事务处理和日志处理可以理解为切面。常用的切面有通知(Advice)与顾问(Advisor)。实际就是对主业务逻辑的一种增强。
(2)织入(Weaving)
织入是指将切面代码插入到目标对象的过程。代理的invoke方法完成的工作,可以称为织入。
(3) 连接点(JoinPoint)
连接点是指可以被切面织入的方法。通常业务接口的方法均为连接点
(4)切入点(PointCut)
切入点指切面具体织入的方法
注意:被标记为final的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(5)目标对象(Target)
目标对象指将要被增强的对象。即包含主业务逻辑的类的对象。
(6)通知(Advice)
通知是切面的一种实现,可以完成简单的织入功能。通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是执行之后执行等。切入点定义切入的位置,通知定义切入的时间。
(7)顾问(Advisor)
顾问是切面的另一种实现,能够将通知以更为复杂的方式织入到目标对象中,是将通知包装为更复杂切面的装配器。
实现AOP配置有四种方案
方案一、我们最先接触的<aop:config><aop:advisor></aop:advisor></aop:config>
方案二、Spring的经典AOP配置方案
方案三、使用AspectJ第三方框架,实现AOP思想 直接配置的AOP
方案四、纯POJO<aop:config>
Spring的经典AOP配置方案
.
(1)。前置和后置增强
package cn.hmy.logger.day01.log; import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class MyBeforeAdvice implements MethodBeforeAdvice{ public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable { System.out.println("before"); } }
package cn.hmy.logger.day01.log; import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class MyMethodAfterAdvice implements AfterReturningAdvice{ public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable { System.out.println("===after====="); } }
package cn.hmy.logger.day01.service; public interface ISomeService { public void doTrancation(); public void dolog(); }
package cn.hmy.logger.day01.serviceImpl; import cn.hmy.logger.day01.service.ISomeService; public class SomeService implements ISomeService{ public void doTrancation() { System.out.println("====doTrancation======"); } public void dolog() { System.out.println("====dolog======"); } }
package cn.hmy.logger.day01.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.hmy.logger.day01.service.ISomeService; import cn.hmy.logger.day01.serviceImpl.SomeService; public class MyTest { public static void main(String[] args) { ApplicationContext ctx=new ClassPathXmlApplicationContext("cn/hmy/logger/day01/applicationContext.xml"); ISomeService ud=(ISomeService)ctx.getBean("proxyFactoryBean"); ud.doTrancation(); ud.dolog(); } }
<?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.xsd"> <bean id="someService" class="cn.hmy.logger.day01.serviceImpl.SomeService"/> <bean id="beforeAdvice" class="cn.hmy.logger.day01.log.MyBeforeAdvice"/> <bean id="afterAdvice" class="cn.hmy.logger.day01.log.MyMethodAfterAdvice"/> <bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="targetName" value="someService"/> <property name="interceptorNames" value="beforeAdvice,afterAdvice"/> </bean> </beans>
环绕增强
异常增强
顾问:
顾问是对通知的封装,有了顾问 我们就可以对特定的方法进行增强,而不是对整个对象的所有方法都进行增强
通过方法名匹配
通过正则匹配
全自动代理和根据beanNames自动代理
此时我们再次触碰到bean时 就不再是通过代理工厂的id来获取实例对象了,而是通过接口实现类的id名称来获取实例对象
通过注解配置AOP方案
1.编写带注解的增强类
package cn.hmy.logger; import java.util.Arrays; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; /** * 1.切入点表达式 execution(【modifiers-pattern?】 访问修饰符 ret-type-pattern 返回值类型 【declaring-type-pattern?】 全限定性类名 name-pattern(param-pattern) 方法名(参数名) 【throws-pattern?】) 抛出异常类型 切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中明显就是方法的签名。注意:表达式中加[]的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号: 符号 意义 * 0至多个任意字符 .. 用在方法参数中,表示任意多个参数 用在包名后,表示当前包及其子包路径 + 用在类名后,表示当前类及其子类 用在接口后,表示当前接口及其实现类 案例: execution(public * *(..)) 指定切入点为:任意公共方法 execution(* set*(..)) 指定切入点为:任何一个以"set"开始的方法 * @author 梦梦 * */ @Aspect public class annotionLogger { private static final Logger log=Logger.getLogger(annotionLogger.class); //定义前置增强 //@Before("execution(public * *(..))") @Before("execution(public * *(..))") public void before(JoinPoint jp){ System.out.println("这是前置增强"); //log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法入参:"+Arrays.toString(jp.getArgs())); } //定义后置增强 @AfterReturning(pointcut="execution(public * *(..))",returning="returnValue") public void afterReturn(JoinPoint jp,Object returnValue){ System.out.println("这是后置增强"); //log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法返回值:"+returnValue); } //环绕增强 /*@Around(value = "execution(public * *(..))") public void myAround(ProceedingJoinPoint pjp){ System.out.println("这是环绕前置增强"); try { pjp.proceed(); } catch (Throwable e) { e.printStackTrace(); } System.out.println("这是环绕后置增强"); }*/ //异常增强 @AfterThrowing(value = "execution(public * *(..))") public void myAfterThrowing(){ System.out.println("这是异常通知"); } }
application/Context.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: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.xsd"> <!--目标对象 --> <bean id="userdaoImpl" class="cn.hmy.dao.impl.UserDaoImpl"/> <!--切面 --> <bean id="MyAspect" class="cn.hmy.logger.annotionLogger"/> <aop:aspectj-autoproxy/> </beans>
测试类
package cn.hmy.test; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.hmy.bean.User; import cn.hmy.dao.UserDao; import cn.hmy.dao.impl.UserDaoImpl; public class aopTest { public static void main(String[] args) throws Exception { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao ud = (UserDao)context.getBean("userdaoImpl"); ud.add(new User()); ud.sel(); } }
通过Aspectj实现纯POJO进行增强处理(AspectJ基于XML的AOP实现)(开发常用)
即不实现任何接口 也不加任何注解
前置增强
package cn.hmy.logger; import java.util.Arrays; import org.apache.log4j.Logger; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; public class annotionLogger { //private static final Logger log=Logger.getLogger(annotionLogger.class); //前置增强 public void myBefore(JoinPoint jp){ System.out.println("这是前置增强"); //log.info("调用"+jp.getTarget()+"的"+jp.getSignature().getName()+"方法。方法入参:"+Arrays.toString(jp.getArgs())); } //后置增强 public void myAfter(){ System.out.println("这是后置增强"); } }
applicationContext.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: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.xsd"> <!--目标对象 --> <bean id="userdaoImpl" class="cn.hmy.dao.impl.UserDaoImpl"/> <!--切面 --> <bean id="MyAspect" class="cn.hmy.logger.annotionLogger"/> <aop:config> <aop:pointcut expression="execution(public void *(cn.hmy.bean.User))" id="myPointcut"/> <aop:aspect ref="MyAspect"> <aop:before method="myBefore" pointcut-ref="myPointcut"/> </aop:aspect> </aop:config> </beans>
测试类
package cn.hmy.test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import cn.hmy.bean.User; import cn.hmy.dao.UserDao; public class aopTest { public static void main(String[] args) throws Exception { ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml"); UserDao ud = (UserDao)context.getBean("userdaoImpl"); ud.add(new User()); } }