AOP的理解:
面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP的功能将切面织入到主业务逻辑中。
所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等。
使用AOP编程思想的好处:
若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。
业务逻辑的增强
如何搭建AOP编程环境:
导入jar包,两个jar包(aop/aopalliance)
附码:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="Index of /schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
Index of /schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 定义目标 -->
<bean id="someServiceimpl" class="cn.msk.service.impl.SomeServiceimpl"></bean>
<!-- 切面:前置通知 -->
<bean id="mymethodBefor" class="cn.msk.aspect.MymethodBefor"></bean>
<!-- 切面:后置通知 -->
<bean id="houzhitouzhi" class="cn.msk.aspect.houzhitouzhi"></bean>
<!-- 切面:环绕通知 -->
<bean id="haunrao" class="cn.msk.aspect.haunrao"></bean>
<!-- 切面:异常通知 -->
<bean id="myyihcang" class="cn.msk.aspect.Myyihcang"></bean>
<!-- 注册切面 -->
<bean id="proxyFactoryBean" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定目标对象 -->
<property name="target" ref="someServiceimpl"></property>
<!-- 指定目标类实现的所有接口 -->
<property name="interfaces" value="cn.msk.service.SomeService"></property>
<!-- 指定切面 -->
<property name="interceptorNames" value="myyihcang"></property>
</bean>
</beans>
业务层:Service
里面有:
SomeService接口/SomeServiceImpl实现类
package cn.msk.service;
public interface SomeService {
void doSome();
String doOther();
}
实现类:
package cn.msk.service.impl;
import cn.msk.service.SomeService;
public class SomeServiceimpl implements SomeService{
@Override
public void doSome() {
System.out.println("SomeServiceimpl.doSome()"+"方法执行"+1/0);
}
@Override
public String doOther() {
System.out.println("SomeServiceimpl.doOther()"+"方法执行");
return "Love";
}
}
定义一个切面包(aspect)存放切面方法:
环绕通知:附码
package cn.msk.aspect;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class haunrao implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation fanhuizhi) throws Throwable {
System.out.println("环绕通知执行前");
//调用目标方法
Object proceed = fanhuizhi.proceed();
if (proceed != null) {
proceed=((String)proceed).toUpperCase();
}
System.out.println("环绕通知执行后");
return proceed;
}
}
后置通知:
package cn.msk.aspect;
import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;
public class houzhitouzhi implements AfterReturningAdvice {
/**
* 切面:后置通知
*
* returnValue:目标方法的返回值
*/
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.out.println("后置方法的返回值");
if (returnValue !=null) {
System.out.println("如果不为空则输出:"+((String)returnValue).toUpperCase());
}
}
}
前置通知:
package cn.msk.aspect;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class MymethodBefor implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("spring框架,把简单的东西复杂化,让别人看不懂!");
}
}
异常通知:
package cn.msk.aspect;
import org.springframework.aop.ThrowsAdvice;
public class Myyihcang implements ThrowsAdvice {
public void afterThrowing(Exception ex) {
System.out.println("异常通知执行");
}
}
Spring、AspectJ和AOP三者之间的关系:
对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。
然而,AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。
所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。
在Spring中使用AOP开发时,一般使用AspectJ的实现方式。
spectJ支持的常见通知:
前置通知
后置通知
环绕通知
异常通知
最终通知
对spring的详细说明:
Spring是什么?为什么使用Spring?
Spring框架是一个为Java应用程序的开发提供了综合、广泛的基础性支持的Java平台。
Spring帮助开发者解决了开发中基础性的问题,使得开发人员可以专注于应用程序的开发。
Spring框架本身亦是按照设计模式精心打造,这使得我们可以在开发环境中安心的集成Spring框架,不必担心Spring是如何在后台进行工作的。
Dependency Injection(DI) 方法使得构造器和JavaBean properties文件中的依赖关系一目了然。
与EJB容器相比较,IoC容器更加趋向于轻量级。
这样一来IoC容器在有限的内存和CPU资源的情况下进行应用程序的开发和发布就变得十分有利。
Spring并没有闭门造车,Spring利用了已有的技术比如ORM框架、logging框架、J2EE、Quartz和JDK Timer,以及其他视图技术。
Spring框架是按照模块的形式来组织的。
由包和类的编号就可以看出其所属的模块,开发者仅仅需要选用他们需要的模块即可。
要测试一项用Spring开发的应用程序十分简单,因为测试相关的环境代码都已经囊括在框架中了。
更加简单的是,利用JavaBean形式的POJO类,可以很方便的利用依赖注入来写入测试数据。
Spring的Web框架亦是一个精心设计的Web MVC框架,为开发者们在web框架的选择上提供了一个除了主流框架比如Struts、过度设计的、不流行web框架的以外的有力选项。
Spring提供了一个便捷的事务管理接口,适用于小型的本地事物处理(比如在单DB的环境下)和复杂的共同事物处理(比如利用JTA的复杂DB环境)。
Spring有哪些特性?
轻量——从大小与开销两方面而言Spring都是轻量的。
完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。
此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。
当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。
你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。
应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。
然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。
Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
所有Spring的这些特征使你能够编写更干净、更可管理、并且更易于测试的代码。
它们也为Spring中的各种模块提供了基础支持。
Spring的重要特征有哪些?
AOP和IoC
Spring的IOC(控制反转),主要的作用是什么,程序中如何体现Spring的控制反转?
Rod Johnson是第一个高度重视以配置文件来管理Java实例的协作关系的人,他给这种方式起了一个名字:控制反转(Inverse of Control,IoC)。
后来Martine Fowler为这种方式起了另一个名称:依赖注入(Dependency Injection),因此不管是依赖注入,还是控制反转,其含义完全相同。
当某个Java对象(调用者)需要调用另一个Java对象(被依赖对象)的方法时,在传统模式下通常有两种做法:
原始做法: 调用者主动创建被依赖对象,然后再调用被依赖对象的方法。
简单工厂模式: 调用者先找到被依赖对象的工厂,然后主动通过工厂去获取被依赖对象,最后再调用被依赖对象的方法。
注意上面的主动二字,这必然会导致调用者与被依赖对象实现类的硬编码耦合,非常不利于项目升级的维护。
使用Spring框架之后,调用者无需主动获取被依赖对象,调用者只要被动接受Spring容器为调用者的成员变量赋值即可,由此可见,使用Spring后,调用者获取被依赖对象的方式由原来的主动获取,变成了被动接受——所以Rod Johnson称之为控制反转。
另外从Spring容器的角度来看,Spring容器负责将被依赖对象赋值给调用者的成员变量——相当于为调用者注入它依赖的实例,因此Martine Fowler称之为依赖注入。
bean的作用域有哪些?各有什么不同?
当通过spring容器创建一个Bean实例时,不仅可以完成Bean实例的实例化,还可以为Bean指定特定的作用域。
Spring支持如下5种作用域:
singleton:单例模式,在整个Spring IoC容器中,使用singleton定义的Bean将只有一个实例。
prototype:原型模式,每次通过容器的getBean方法获取prototype定义的Bean时,都将产生一个新的Bean实例。
request:对于每次HTTP请求,使用request定义的Bean都将产生一个新实例,即每次HTTP请求将会产生不同的Bean实例。
只有在Web应用中使用Spring时,该作用域才有效。
session:对于每次HTTP Session,使用session定义的Bean豆浆产生一个新实例,同样只有在Web应用中使用Spring时,该作用域才有效。
globalsession:每个全局的HTTP Session,使用session定义的Bean都将产生一个新实例。典型情况下,仅在使用portlet context的时候有效。
同样只有在Web应用中使用Spring时,该作用域才有效。
其中比较常用的是singleton和prototype两种作用域。
对于singleton作用域的Bean,每次请求该Bean都将获得相同的实例。
容器负责跟踪Bean实例的状态,负责维护Bean实例的生命周期行为;如果一个Bean被设置成prototype作用域,程序每次请求该id的Bean,Spring都会新建一个Bean实例,然后返回给程序。
在这种情况下,Spring容器仅仅使用new 关键字创建Bean实例,一旦创建成功,容器不在跟踪实例,也不会维护Bean实例的状态。
如果不指定Bean的作用域,Spring默认使用singleton作用域。
Java在创建Java实例时,需要进行内存申请;销毁实例时,需要完成垃圾回收,这些工作都会导致系统开销的增加。
因此,prototype作用域Bean的创建、销毁代价比较大。而singleton作用域的Bean实例一旦创建成功,可以重复使用。因此,除非必要,否则尽量避免将Bean被设置成prototype作用域。
属性注入方式有哪三种?
set注入
构造注入
工厂注入
使用XML装配(xml是最常见的spring应用系统配置源)
几种spring容器都支持使用xml装配bean,包括:
1.XmlBeanFactory:调用InputStream载入上下文定义文件。
2.ClassPathXmlApplicationContext:从类路径载入上下文定义文件。
3.XmlWenApplicationContext:从web应用上下文中载入定义文件。
Spring中怎样实现自动扫描?
<context:component-scan base-package="cn.itcast" />
@Configuration把一个类作为一个IoC容器,它的某个方法头上如果注册了@Bean,就会作为这个Spring容器中的Bean。
@Scope注解 作用域
@Lazy(true) 表示延迟初始化
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)
@Repository用于标注数据访问组件,即DAO组件。
@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Scope用于指定scope作用域的(用在类上)
@PostConstruct用于指定初始化方法(用在方法上)
@PreDestory用于指定销毁方法(用在方法上)
@Resource 默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
@DependsOn:定义Bean初始化及销毁时的顺序。
@Primary:自动装配时当出现多个Bean候选者时,被注解为@Primary的Bean将作为首选者,否则将抛出异常
@Autowired 默认按类型装配,如果我们想使用按名称装配,
@PostConstruct 初始化注解
@PreDestroy 摧毁注解默认单例启动就加载
静态代理和动态代理的关系
静态代理通常只代理一个类,动态代理是代理一个接口下的多个实现类。
静态代理事先知道要代理的是什么,而动态代理不知道要代理什么东西,只有在运行时才知道。
动态代理是实现 JDK 里的 InvocationHandler 接口的 invoke 方法,但注意的是代理的是接口,也就是你的
业务类必须要实现接口,通过 Proxy 里的 newProxyInstance 得到代理对象。
还有一种动态代理 CGLIB,代理的是类,不需要业务类继承接口,通过派生的子类来实现代理。通过在运行
时,动态修改字节码达到修改类的目的。
AOP 编程就是基于动态代理实现的,比如著名的 Spring 框架、 Hibernate 框架等等都是动态代理的使用例子。
通知有哪几种类型,运行顺序是什么?
Before:在目标方法被调用之前做增强处理,@Before只需要指定切入点表达式即可。
AfterReturning:在目标方法正常完成后做增强,@AfterReturning除了指定切入点表达式后,还可以指定一个返回值形参名returning,代表目标方法的返回值。
AfterThrowing:主要用来处理程序中未处理的异常,@AfterThrowing除了指定切入点表达式后,还可以指定一个throwing的返回值形参名,可以通过该形参名,
来访问目标方法中所抛出的异常对象。
After:在目标方法完成之后做增强,无论目标方法时候成功完成。@After可以指定一个切入点表达式。
Around:环绕通知,在目标方法完成前后做增强处理,环绕通知是最重要的通知类型,像事务,日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint