1. Spring
轻量级Java EE开源框架,为了解决企业级应用程序开发的复杂性而创建
2. Spring内容
1、AOP实现
2、IOC容器
3、对现有框架的一些支持
3. Spring体系结构
3.1 Bean
就是特定格式的类,spring就是对bean的管理
3.2 Context
上下文可以视作Bean工厂上又一层封装,主要面向企业级开发中的实际需求,例如加载资源、处理事件、远程调用支持等
4. Spring是面向Bean的编程
IOC(Inversion of Control):控制翻转
AOP(Aspect Oriented Programming):面向切面编程
5. Spring在分层体系中的作用
6. 控制反转(IOC)\依赖注入(DI)
什么是依赖:
相当于如果我们想要要实现一个功能,于是我们创建了类A,但是在类A的实现过程中用到了类B,这样B就成为了A的依赖。这样导致了A与B之间有非常强的耦合,即A的创建离不开B,如果需求变更了,A不光依赖B,A还需要扩展C的功能,但是因为在A的代码中写死了,这样子代码就丧失了灵活性也就是说代码耦合性过高。
如果想要将A、B解耦合则可以在A、B之间加入一个B的工厂用来提供B的具体实现类,这样子具体返回什么样的B则是变成了由B的工厂决定了,A的代码不再涉及到B,这就完成了A和B的解耦合。具体可以参考设计模式中的工厂模式。
那么问题来了,如果每次遇到这种问题都要创建一个工厂类,那样子会增加很多开发的工作量,Spring则提供了解决这个问题的方式。
示例:
需要用到的spring-framwork第三方依赖:
spring-aop-3.2.1.RELEASE.jar
spring-beans-3.2.1.RELEASE.jar
spring-context-3.2.1.RELEASE.jar
spring-core-3.2.1.RELEASE.jar
spring-expression-3.2.1.RELEASE.jar
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-3.1.xsd ">
<!-- 定义userDaoImpl对象,并指定id为userDaoImpl -->
<bean id="userDaoImpl" class="cn.bdqn.biz.dao.impl.UserDaoImpl" />
<!-- 定义User对象,并指定id为user -->
<bean id="user" class="cn.bdqn.biz.pojo.User" />
<!-- 定义UserServiceImpl对象,并指定id为userServiceImpl -->
<bean id="userServiceImpl" class="cn.bdqn.biz.service.impl.UserServiceImpl">
<!--为userServiceImpl的userDao属性赋值,需要注意的是,这里要调用setUserDao()方法 -->
<property name="userDao">
<!-- 引用id为userDao的对象为userServiceImpl的userDao属性赋值 -->
<ref bean="userDaoImpl" />
</property>
</bean>
</beans>
具体代码:
public void static main(String[] args) {
// 使用ApplicationContext接口的实现类ClassPathXmlApplicationContext加载Spring配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("resources/applicationContext.xml");
// 通过ApplicationContext接口的getBean()方法获取id或name为user的Bean实例
User user = (User) ctx.getBean("user");
user.setId(1);
user.setUserName("test");
user.setPassword("123456");
user.setEmail("test@pbdevj.com");
// 通过ApplicationContext接口的getBean()方法获取id或name为userServiceImpl的Bean实例
UserService userService = (UserService) ctx.getBean("userServiceImpl");
userService.addNewUser(user);
}
UserDao接口:
public interface UserDao {
public void save(User user);
}
UserDao实现:
public class UserDaoImpl implements UserDao {
public void save(User user) {
// 这里并未实现完整的数据库操作,仅为说明问题
System.out.println("保存用户信息到数据库");
}
}
UserService接口:
public interface UserService {
public void addNewUser(User user);
}
UserService实现:
public class UserServiceImpl implements UserService {
// 声明接口类型的引用,和具体实现类解耦合
private UserDao userDao;
// userDao 属性的setter访问器,会被Spring调用,实现设值注入
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
public void addNewUser(User user) {
// 调用用户DAO的方法保存用户信息
userDao.save(user);
}
}
User类:
public class User{
private Integer id; // 用户ID
private String userName; // 用户名
private String password; // 密码
private String email; // 电子邮件
// getter & setter
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
xml配置文件主要做了这么几件事:首先声明了3个bean对象,分别对应userDaoImpl,user,userServiceImpl;其中userServiceImpl类中的userDao属性的实例化对象为userDaoImpl。这样子在使用了Spring框架之后,Spring自动将userDaoImpl注入到UserServiceImpl的userDao对象中去,也就相当于从xml配置层面控制了userDao的具体实例化对象的类型,起到了工厂的作用。
然后怎么理解IOC呢,IOC翻译过来是控制反转,原先选择哪个类B去创建依赖于A,但是现在A放弃了这个控制权,转而通过spring bean容器来控制类B的创建,这就实现了控制反转。
IOC使用两大关键技术一个设计模式:
- JDOM
- 反射机制
- 工厂模式(单例模式):这里需要注意spring用单利模式创建bean,这样的话bean中的属性是被所有人共享的。
Bean的作用域:
默认情况为单例:scope=“singleton”
<bean id="user" class="com.User" />
<bean id="user" class="com.User" scope="singleton"/>
ApplicationContext:
ApplicationContext是BeanFactory的子接口,Application扩展很多BeanFactory不具备的功能。
实现类 ClassPathXmlApplicationContext
实现类 FileSystemXmlApplicationContext
注:Spring 还支持多配置文件整合
7. 面向切面编程(AOP)
原理:通过将复杂的需求分解出不同方面,将散布在系统中的公共功能集中解决。是一种通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态添加功能的技术。
IOC解决的是对类的选择,一定会调用该类的方法;AOP解决的是控制核心业务以外的方法是否使用,不一定会调用其他类的方法。
使用“横切”技术,AOP把软件系统分为两部分:
- 核心关注点
- 横切关注点
实现方法:动态代理设计模式
Spring提供了4中实现AOP的方式
1. 基于代理的经典AOP
2. 纯POJO切面
3. @AspectJ注解驱动的切面
4. 注入式AspectJ切面
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"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
<bean id="serviceLogging" class="com.pb.aop.ServiceLogging"></bean>
<bean id="userService" class="com.pb.service.UserService"></bean>
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(public * com.pb.service.*.*(..))" />
<aop:aspect ref="serviceLogging">
<aop:before method="beforeService" pointcut-ref="servicePointcut"/>
<aop:after-returning method="afterReturning" pointcut-ref="servicePointcut" returning="returnVal"/>
<aop:after-throwing method="afterThrowing" pointcut-ref="servicePointcut" throwing="ex"/>
<aop:after method="after" pointcut-ref="servicePointcut"/>
<aop:around method="around" pointcut-ref="servicePointcut"/>
</aop:aspect>
</aop:config>
</beans>
spring定义切入点的方法:
切入点:连接点的查询条件
<aop:pointcut id="servicePointcut" expression="execution(public * com.pb.service.*.*(..))" />
表达式匹配规则举例
public * addUser(com.pb.entity.User): "*" 表示匹配所有类型的返回值
public void *(com.pb.entity.User): "*"表示匹配所有方法名
public void addUser(..): ".." 表示匹配所有参数个数和类型
* com.pb.service.*.*(..): 匹配com.pb.servie包下所有类的所有方法
* com.pb.service..*(..): 匹配com.pb.service包及子包下所有类的所有方法
ServiceLogging类
package com.pb.aop;
import org.apache.log4j.Logger;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class ServiceLogging {
private Logger logger = Logger.getLogger(ServiceLogging.class);
public void beforeService(JoinPoint joinPoint){
logger.info("前置增强处理被执行");
logger.info("连接点对象:"+joinPoint.getTarget().getClass().getSimpleName());
logger.info("连接点方法:"+joinPoint.getSignature());
logger.info("连接点方法参数:"+joinPoint.getArgs()[0]);
}
public void after(){
logger.info("最终增强处理被执行");
}
public void afterReturning(Object returnVal){
logger.info("后置增强处理被执行");
logger.info("后置增强处理:方法返回值为:"+returnVal);
}
public void afterThrowing(Exception ex){
logger.info("业务方法抛出了异常,异常增强处理被执行");
logger.info("抛出的异常为:"+ex.getMessage());
}
public Boolean around(ProceedingJoinPoint pjp) throws Throwable{
logger.info("环绕增强处理:目标方法执行前织入");
logger.info("环绕增强处理:目标方法的参数:"+pjp.getArgs()[0]);
Boolean b = null;
if(true){
b = (Boolean)pjp.proceed(pjp.getArgs());
}
logger.info("环绕增强处理:目标方法的返回值为:"+b);
logger.info("环绕增强处理:目标方法执行后织入");
return b;
}
}
说明:JoinPoint对象用于获取连接点信息。连接点就相当于被增强的那个具体的实例。Object returnVal是方法执行完之后的返回值。对应xml文档中的returning,表示将方法返回值赋值给afterReturning方法的returnVal参数。
<aop:after-returning method="afterReturning" pointcut-ref="servicePointcut" returning="returnVal"/>
Spring AOP的主要工作
- 通过Advice(增强)描述横切逻辑和方法的具体注入点(方法前、方法后、方法两端等等)
- Spring AOP通过Pointcut(切点)指定在哪些类的哪些方法上织入横切逻辑
- Spring通过切面将Pointcut和Advice两者结合起来
- Spring利用JDK动态代理技术或GCLib为目标Bean创建织入切面的代理对象