Spring
1 什么是Spring
产生于2003年,是一个分层的Javase/ee一站式轻量级开源框架。从功能的角度来定义的,从本质意义上来讲,Spring是一个Java库,它提供了一个软件框架,这个框架目的是使软件之间的逻辑更加清晰,配置更灵活。
2 Spring的优点
2.1 简化开发,方便解耦(IOC)
Spring本身是一个工厂(容器),创建管理对象以及维护对象之间关系。
方便解耦:体现了高内聚,低耦合。内聚:每个模块尽可能独立完成自己的功能,不依赖于模块外部的代码。模块设计推荐采用高内聚。耦合:模块与模块之间接口的复杂程度,模块之间联系越复杂耦合度越高,牵一发而动全身。如:一个用户 用户类user里面的方法全部是用户相关的,比如用户登录方法,调用了获取用户基本信息方法, 这些方法内部相互依赖,相互调用,依赖程度很高,但是却没有定义订单 支付的方法,这就是高内聚。用户的支付模块,订单模块,不直接关联,用另外一个或者多个service去调用,支付和订单不直接调用,从而解耦合,订单业务修改了,支付调用的地方不用修改,因为是另外一个类的的作为中间人去调用的,这就是低耦合。
2.2 支持AOP编程
面向切面编程,拦截进行添加增强。简单理解就是在运行时,动态的将代码切入到类的指定方法的指定位置上,这种思想就是面向切面的编程思想。
2.3 支持优秀框架集成
不排斥各种优秀框架,如mybatis,springmvc,hiberbate,quartz,struts2等等一系列的优秀框架的直接支持。
2.4 简化Javaee的API
spring框架是对javaee良好补充,如对JDBC、JavaMail等,提供了封装,使这些API应用起来非常简单。
2.5 支持事务声明
只需要通过配置就可以完成对事物的管理,不需要手动编程。
2.6 支持junit集成
可以通过注解方便的测试Spring程序。
3 Spring Framework核心
3.1 IOC(控制反转)
IOC 全称为 Inversion Of Control,也叫做
DI(Dependency Injection
)依赖注入。
IOC的定义:就是由 Spring IOC 容器来负责对象的生命周期和对象之间的关系。
一般我们需要某个对象是采用new的方式直接创建,这个过程复杂而又繁琐,我们又必须面对每个环节,还需要销毁它,并且该对象和它依赖的对象耦合在一起。其实我们依赖一个对象并不是依赖他本身而是依赖他提供的服务,我们需要它的时候它能提供服务,至于是我们自己手动创建的还是别人给我们的并不重要。当我们需要的时候别人给我们,管理都交给了别人,相当于别人为自己服务,给我们的人就是IOC容器。
IOC为被注入对象提供被依赖对象也有如下几种方式:构造方法注入、setter方法注入、接口注入。
构造器注入:被注入的对象通过在其构造方法中声明依赖对象的参数列表,让外部知道它需要哪些依赖对象。
setter 方法注入:当前对象只需要为其所依赖的对象提供相对应的 setter 方法,就可以通过该方法将相应的依赖对象设置到被注入对象中。
接口方式注入:它需要被依赖的对象实现不必要的接口,带有侵入性。一般都不推荐这种方式。
3.2 Spring IoC的五个核心体系
(1)Resource体系和ResourceLoader体系:Resource体系是对资源的抽象,每个实现类都代表一种资源的访问策略,如ClasspathResource 、 InputStreamResource、URLResource ,FileSystemResource 等。ResourceLoader体系进行统一资源加载,实现类如:ClassRelativeResourceLoader、FileSystemResourceLoader,PathMatchingResourcePatternResolver。
(2)BeanFactory体系:是 IOC 必备的数据结构,BeanDefinition 是她的基本结构,它内部维护着一个 BeanDefinition map ,并可根据 BeanDefinition 的描述进行 bean 的创建和管理。BeanFacoty 有三个直接子类 ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory,DefaultListableBeanFactory 是最终默认实现,实现了所有接口。
(3)BeanDefinition体系:用来描述 Spring 中的 Bean 对象
(4)BeanDefinitionReader体系:读取 Spring 的配置文件的内容,并将其转换成 IOC 容器内部的数据结构:BeanDefinition。
(5)ApplicationContext系统:这个就是Spring容器,继承 BeanFactory,继承 MessageSource,提供国际化的标准访问策略;继承 ApplicationEventPublisher ,提供强大的事件机制;扩展 ResourceLoader,可以用来加载多个 Resource,可以灵活访问不同的资源。
3.3 AOP(面向切面)
什么是AOP:采用横向抽取方式,在运行期间将增强代码添加到目标对象方法的一种思想,底层采用动态代理。
常见AOP框架:Spring aop,aspectj,jboss aop
AOP常见应用场景:事务管理(声明式事务),日志系统,性能监测
切面:可以这么理解编程中,对象与对象之间,方法与方法之间,模块与模块之间都是一个个切面。
AOP相关专业术语如下:
target(目标对象) | 需要添加增强代码的对象 |
joinpoint(连接点) | 可以添加增强代码的目标方法 |
pointcut(切入点) | 一组集中的joinpoint,定义了相应的 Advice 将要发生的地方 |
advice(通知) | 实现特定接口的增强代码 |
aspect(切面) | 增强代码和切入点之间形成的逻辑面 |
weaver(织入) | 增强代码添加到切入点过程 |
注意:Spring只支持方法类型的joinpoint
advice的类型:
before | 在joinpoint前被执行的advice |
after-returning | 在joinpoint正常返回后执行的advice |
after-throwing | 当joinpoint抛出异常后执行的advice |
after(final) | 无论一个joinpoint是正常退出还是发生了异常都会执行的advice |
around | 在joinpoint前和joinpoint退出后都会执行的advice |
introduction | 可以为原有的对象添加属性和方法 |
3.4 代理
什么是代理:为其他对象提供一种代理以控制对这个对象的访问,可以实现访问目标之前或之后实现预处理和后续处理。
(1)静态代理:接口编写代理类。
特点:代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多.同时,一旦接口增加方法,目标对象与代理对象都要维护。
(2)动态代理(JDK代理,接口代理):接口 + 委托类
特点:代理对象,不需要实现接口;代理对象的生成,是利用JDK的API,动态的在内存中构建代理对象(需要我们指定创建代理对象/目标对象实现的接口的类型
package dao.imple;
import dao.UserDao;
public class UserDaoImple implements UserDao {
@Override
public void add(String str) {
System.out.println("添加" + str);
}
@Override
public int remove(int i) {
System.out.println("删除");
return i-1;
}
}
package test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import dao.UserDao;
import dao.imple.UserDaoImple;
public class AOPTest {
public static void main(String[] args) {
//1. 创建委托类对象
UserDao userDao = new UserDaoImple();
//2. 给委托类对象创建代理对象(实现同一个接口)
UserDao proxy = (UserDao)Proxy.newProxyInstance(
//ClassLoader loader:类加载器
userDao.getClass().getClassLoader(),
//Class<?>[] interfaces:委托类对象实现的接口们
userDao.getClass().getInterfaces(),
//InvocationHandler h:拦截方法的处理
new InvocationHandler() {
//Object proxy:代理对象 Method method方法的描述对象 Object[] args方法参数
//执行代理类对象的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法前执行");
//手动调用委托类的方法
Object obj = method.invoke(userDao, args);
System.out.println("方法后执行");
return obj;
}
});
//3. 访问代理对象的方法(实际上是访问委托类的方法)
proxy.add("添加");
proxy.remove(2);
}
}
(3)Cglib动态代理:代理对象继承委托类(知晓委托类的方法)
Cglib代理,也叫作子类代理,在内存中构建一个子类对象从而实现对目标对象功能的扩展。静态代理和动态代理目标对象都需要实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理。
需要使用字节码增强jar包(asm.jar/cglib.jar),spring的核心包pring-core包括了
cglib功能。
注意:代理的类不能为final,否则报错;目标对象的方法如果为final/static,那么就不会被拦截,即不会执行目标对象额外的业务方法
package dao.imple;
public class UserDaoImple2{
public void add() {
System.out.println("添加" );
}
public void remove() {
System.out.println("删除");
}
}
package test;
import java.lang.reflect.Method;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import dao.imple.UserDaoImple2;
public class AOPTest {
public static void main(String[] args) {
//1. 创建委托类对象
UserDaoImple2 userDaoImple2 = new UserDaoImple2();
//2. 给委托类创建代理对象(代理类对象继承委托类)
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(UserDaoImple2.class);
//方法拦截处理
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] arg2, MethodProxy methodProxy ) throws Throwable {
System.out.println("方法前执行");
Object obj = method.invoke(userDaoImple2, arg2);
System.out.println("方法后前执行");
return obj;
}
});
UserDaoImple2 proxy = (UserDaoImple2)enhancer.create();
//3. 访问代理对象的方法(实际上是访问委托类的方法)
proxy.add();
proxy.remove();
}
}
在Spring的AOP编程中:如果加入容器的目标对象有实现接口,用JDK代理。如果目标对象没有实现接口,用Cglib代理
3.5 IOC及AOP实例
需要的基本jar包:
spring-aop: spring aop框架 spring-aspects:aspectj框架(规范) aspectjweaver:aspectj框架中织入的实现
aopalliance:aop联盟(制定通知规范) commons-logging日志
委托类
package service.imple;
import dao.UserDao;
import service.UserService;
/**
* @Description 委托类
* @author refuel
* @version v1.0
*/
public class UserServiceImple implements UserService {
UserDao dao;
public UserServiceImple(UserDao dao) {
this.dao=dao;
}
@Override
public void add(String str) {
dao.add(str);
}
@Override
public int remove(int i) {
return dao.remove(i);
}
}
package service;
public interface UserService {
void add(String str);
int remove(int i);
}
package dao.imple;
import dao.UserDao;
public class UserDaoImple implements UserDao {
@Override
public void add(String str) {
System.out.println("添加" + str);
}
@Override
public int remove(int i) {
System.out.println("删除");
return i-1;
}
}
package dao;
public interface UserDao {
void add(String str);
int remove(int i);
}
增强代码类
package advice;
/**
* @Description 委托类
* @author refuel
* @version v1.0
*/
public class Myaspect {
public void begin() {
System.out.println("开始");
}
public void end() {
System.out.println("结束");
}
}
applicationContext.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!-- schema约束 -->
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="userdao" class="dao.imple.UserDaoImple"></bean>
<!-- 目标类对象 -->
<bean id="userservice" class="service.imple.UserServiceImple">
<constructor-arg name="dao" ref="userdao"></constructor-arg>
</bean>
<!-- 增强类对象 -->
<bean id="myaspect" class="advice.Myaspect"></bean>
<!-- 织入增强代码 -->
<aop:config >
<!-- 切入点:寻找增强代码
id:唯一标识
expression: 设定作为切入点方法
execution(返回值 包名.类名.方法名(参数列表)) * 通配
execution(* service.impl.UserServiceImpl.*(..))
execution(* service..*(..))
-->
<aop:pointcut expression="execution(* service.imple.UserServiceImple.*(..))" id="userPintCut"/>
<!-- 切面:切入点加增强代码
aop:怎样添加增强代码到切入点
ref:增强代码对象
-->
<aop:aspect ref="myaspect">
<aop:after method="begin" pointcut-ref="userPintCut"/>
<aop:after-returning method="end" pointcut-ref="userPintCut"/>
<aop:declare-parents types-matching="" implement-interface=""/>
</aop:aspect>
</aop:config>
</beans>
测试类
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
public class AOPTest {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService bean = ac.getBean("userservice", UserService.class);
bean.add("a");
System.out.println(bean.remove(5));
}
}
上述配置文件的注入方式为手动注入,可以配置自动注入如下
<bean id="userservice" class="service.impl.UserServiceImpl" autowire=""></bean>
no: 默认,不自动注入
byType: 按照类型注入,从整个ioc容器中寻找属性同类型的值实现注入,
如果找不到,就不注入;如果找到一个,则注入成功; 如果找到多个,则抛出异常。
byName: 按照名称注入,从整个ioc容器中寻找属性同名的值实现注入,
如果找不到,就不注入;
如果找到一个,则注入成功。
constructor: 自动注入中的构造注入(等同于设值注入中的byType)
3.6 注解实现装配
常见注解:
<bean class=""></bean> ---> @Component (组件) 默认id就是类名
<bean id="" class=""></bean> --> @Component("id")
@Controller: 表示层 @Service: 业务逻辑层 @Repository: 数据持久层
applicationContext.xml配置文件
注解注入类型:
@Autowired: 按照类型注入
@Qualifier("id"):按照名称注入
@Resources(name="id"):按照名称注入
applicationContext.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
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.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描配置了注解的对象,将它注册为spring的bean放置在ioc容器中 -->
<context:component-scan base-package="cn.refuel.service,cn.refuel.advice"></context:component-scan>
<!-- 切面配置的注解生效 -->
<aop:aspectj-autoproxy/>
</beans>
增强代码类
package advice;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
public class CustomAspect {
//环绕(手动调用方法)
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) {
Object proceed = null;
try {
System.out.println("前置增强");
//执行方法(放行目标方法)
proceed = joinPoint.proceed();
System.out.println("后置增强");
} catch (Throwable e) {
System.out.println("异常增强");
}finally {
System.out.println("最终增强");
}
return proceed;
}
//配置切入点
@Pointcut("execution(* service..*(..))")
public void pointCut() {}
@Before("pointCut()")
public void before(JoinPoint joinPoint) {
Object target = joinPoint.getTarget();
System.out.println("target:"+target);
String name = joinPoint.getSignature().getName();
System.out.println("name"+name);
System.out.println("前置增强");
}
@AfterReturning("pointCut()")
public void afterReturning() {
System.out.println("后置增强");
}
@AfterThrowing("pointCut()")
public void afterThrowing() {
System.out.println("异常增强");
}
@After("pointCut()")
public void after() {
System.out.println("最终增强");
}
}
委托类
package cn.refuel.service.imple;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import cn.refuel.dao.UserInfoDao;
import cn.refuel.service.UserInfoService;
//目标类
@Service("userinfoservice")
public class UserInfoServiceImple implements UserInfoService {
@Autowired
/* @Qualifier("userinfodao") */
UserInfoDao userInfoDao;
public UserInfoServiceImple(UserInfoDao userInfoDao) {
this.userInfoDao=userInfoDao;
}
@Override
public void addUser() {
System.out.println("add");
}
@Override
public void deleteUser() {
System.out.println("remove");
}
}
package cn.refuel.service;
public interface UserInfoService {
void addUser();
void deleteUser();
}
package cn.refuel.dao.imple;
import org.springframework.stereotype.Repository;
import cn.refuel.dao.UserInfoDao;
@Repository("userinfodao")
public class UserInfoDaoImple implements UserInfoDao {
@Override
public void addUser() {
System.out.println("添加");
}
@Override
public void deleteUser() {
System.out.println("删除");
}
}
package cn.refuel.dao;
public interface UserInfoDao {
void addUser();
void deleteUser();
}
测试类
package cn.refuel.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import cn.refuel.service.UserInfoService;
public class MainTest {
public static void main(String[] args) {
//加载配置文件,获取IOC容器
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
UserInfoService bean = ac.getBean("userservice", UserInfoService.class);
bean.addUser();
}
}