通过对Spring的学习,我认为以下是Spring最重要的方面
Spring框架的两个核心特征:反向控制(IoC)和面向切面编程(AOP)。它是一个轻量级容器框架,即Spring是非侵入式的,简单的说就是离开Spring照样工作,这一点不同于EJB。
Spring提倡使用反向控制(IoC)来实现松耦合,对象不是从容器中查找它的依赖类,而是主动将它的依赖类注入。
AOP是一种编程技术,用来在系统中提升业务的分离,例如将日志、事务管理和安全等服务模块化,然后把它们声明式地应用在需要的地方,结果是业务更专一。
Spring核心容器为Spring框架提供基础功能,其中BeanFactory类是核心。如下应用:
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
下面从反向控制(IoC)说起,
任何重要系统如果需要多个类协作完成业务,通常,每个对象都要自己负责得到它的合作对象,这样导致代码耦合度高而且难以测试,使用反向控制(IoC),对象的依赖都是在对象创建时从由复制协调系统中各个对象的外部实体提供的,即依赖被注入到对象中,关于对象如何得到它的协作对象的责任反转了。详细情况通过以下示例说明:
定义一个接口UserDao
public interface UserDao {
public void save(String username, String password);
}
它有两个实现,分别是UserDao4OracleImpl和UserDao4MySqlImpl
public class UserDao4OracleImpl implements UserDao {
public void save(String username, String password) {
System.out.println("--------UserDao4MySqlImpl.save()-------");
}
}
public class UserDao4MySqlImpl implements UserDao {
public void save(String username, String password) {
System.out.println("--------UserDao4MySqlImpl.save()-------");
}
}
然后再定义一个接口UserManager
public interface UserManager {
public void save(String username, String password);
}
它的实现为UserManagerImpl,通过构造方法引入对象
public class UserManagerImpl implements UserManager {
private UserDao userDao;
public UserManagerImpl(UserDao userDao) {
this.userDao = userDao;
}
public void save(String username, String password) {
this.userDao.save(username, password);
}
}
最后在客户端应用,即Client类
public class Client {
public static void main(String[] args) {
UserManager userManager = new UserManagerImpl(new UserDao4MySqlImpl());
userManager.save("张三", "123");
}
}
如果我们需要变换数据库,则需要改变Client类中的代码,
public class Client {
public static void main(String[] args) {
UserManager userManager = new UserManagerImpl(new UserDao4OracleImpl());
userManager.save("张三", "123");
}
}
这就是我们所说的对象自己负责得到它的合作对象,即代码耦合度高。
下面看看引入Spring的效果:
public class Client {
public static void main(String[] args) {
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
UserManager userManager = (UserManager)factory.getBean("userManager");
userManager.save("张三", "123");
}
}
同样,改变数据库时,代码不需要任何改变,只需在配置文件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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="userDao4MySqlImpl" class="com.jzh.spring.dao.UserDao4MySqlImpl"/>
<bean id="userDao4OracleImpl" class="com.jzh.spring.dao.UserDao4OracleImpl"/>
<bean id="userManager" class="com.jzh.spring.manager.UserManagerImpl">
<!-- 构造方法注入
<constructor-arg ref="userDao4OracleImpl"/>
-->
<property name="userDao" ref="userDao4OracleImpl"/>
</bean>
</beans> userDao4MySqlImpl
下面接着说AOP
Spring的AOP框架使分散在系统中的功能块放到一个地方——切面。
AOP使你在一个地方定义通用功能(安全、事物管理等),声明式定义何时何地应用这些功能,而不需要在应用的地方修改代码。
简单介绍以下AOP术语:
切面(Aspect)——要实现的交叉功能。例如日志记录。
连接点(Joinpoint)——应用程序执行过程中插入切面的地点。Spring中指方法调用。
通知(Advice)——通知切面的实际实现。它通知应用系统新的行为。
切入点(Pointcut)——定义通知应该应用在那些连接点。
引入(Introduction)——允许你为已存在类添加新方法和属性。
目标对象(Target)——目标对象是被通知对象,它既可以是你编写的类也可以是你要添加定制行为的第三方类。
代理(Proxy)——是将通知应用到目标对象后创建的对象。
织入(Weaving)——是将切面应用到目标对象从而创建一个新的代理对象的过程。
不是所有AOP框架都按不同样方式实现,本人主要学习通过AspectJ来实现AOP:
首先采用注解方式实现AOP
定义切面:
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
/**
* 定义Aspect
* @author Administrator
*
*/
@Aspect
public class SecurityHandler {
/**
* 定义Pointcut,Pointcut的名称就是allAddMethod,此方法不能有返回值和参数,该方法只是一个
* 标识
*
* Pointcut的内容是一个表达式,描述那些对象的那些方法(订阅Joinpoint)
*/
@Pointcut("execution(* add*(..)) || execution(* del *(..))")
private void allAddMethod(){};
/**
* 定义Advice,标识在那个切入点何处织入此方法
*/
@Before("allAddMethod()")
private void checkSecurity() {
System.out.println("----------checkSecurity()---------------");
}
}
然后将切面和目标对象配置到Ioc容器中,启用AspectJ对Annotation的支持
<?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"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<aop:aspectj-autoproxy/> 启用AspectJ对Annotation的支持
<bean id="securityHandler" class="com.jzh.spring.SecurityHandler"/>
<bean id="userManager" class="com.jzh.spring.UserManagerImpl"/>
</beans>
然后应用:
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Client {
public static void main(String[] args) {
BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext.xml");
UserManager userManager = (UserManager)factory.getBean("userManager");
userManager.addUser("张三", "123");
userManager.deleteUser(1);
}
}
当然也可以采用配置文件的方式
<aop:config>
<aop:aspect id="security" ref="securityHandler">
<aop:pointcut id="allAddMethod" expression="execution(* com.bjsxt.spring.UserManagerImpl.add*(..))"/>
<aop:before method="checkSecurity" pointcut-ref="allAddMethod"/>
</aop:aspect>
</aop:config>
此时,切面不需要任何注解:如下
public class SecurityHandler {
private void checkSecurity() {
System.out.println("----------checkSecurity()---------------");
}
}
另外需要特别注意的是:
spring对AOP的支持
Aspect默认情况下不用实现接口,但对于目标对象(UserManagerImpl.java),在默认情况下必须实现接口
如果没有实现接口必须引入CGLIB库
我们可以通过Advice中添加一个JoinPoint参数,这个值会由spring自动传入,从JoinPoint中可以取得
参数值、方法名等等
spring对AOP的支持
1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP
3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
如何强制使用CGLIB实现AOP?
* 添加CGLIB库,SPRING_HOME/cglib/*.jar
* 在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>
JDK动态代理和CGLIB字节码生成的区别?
* JDK动态代理只能对实现了接口的类生成代理,而不能针对类
* CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final