spring
1. 简介
Spring是一个开源的Java EE开发框架。Spring框架的核心功能可以应用在任何Java应用程序中,但对Java EE平台上的Web应用程序有更好的扩展性。Spring框架的目标是使得Java EE应用程序的开发更加简捷,通过使用POJO为基础的编程模型促进良好的编程风格。
2. Spring优点
- 轻量级:Spring在大小和透明性方面绝对属于轻量级的,基础版本的Spring框架大约只有2MB。
- 控制反转(IOC):Spring使用控制反转技术实现了松耦合。依赖被注入到对象,而不是创建或寻找依赖对象。
- 面向切面编程(AOP): Spring支持面向切面编程,同时把应用的业务逻辑与系统的服务分离开来。
- 容器:Spring包含并管理应用程序对象的配置及生命周期。
- MVC框架:Spring的web框架是一个设计优良的web MVC框架,很好的取代了一些web框架。
- 事务管理:Spring对下至本地业务上至全局业务(JAT)提供了统一的事务管理接口。
- 异常处理:Spring提供一个方便的API将特定技术的异常(由JDBC, Hibernate, 或JDO抛出)转化为一致的、Unchecked异常。
3. spring三大核心组件
3.1 控制反转(IOC)
控制反转(IoC=Inversion of Control),就是由spring容器控制程序之间的(依赖)关系,而非传统实现中,由程序代码直接操控。这也就是所谓“控制反转”的概念所在:(依赖)控制权由应用代码中转到了外部容器,控制权的转移,是所谓反转。
3.2 依赖注入(DI)
依赖注入有几种方式:属性注入;构造器注入;bean的方式。
3.2.1 属性注入
public void User{
String name;
public void setName(String name) {
this.name = name;
}
}
注入时调用set()方法就行。
3.2.2 构造器注入
public class User {
String name;
public User(String name) {
this.name = name;
}
}
注入时 new User(“Tim”) 即可。
3.2.3 bean方式
配置文件(spring.xml):
<bean id="user" class="test.User">
<property name="name" value="cup"></property>
</bean>
Controller中获取:
/读取配置文件(将配置文件中的bean加载进内存)
ApplicationContext ctx = new ClassPathXmlApplicationContext("/testSpring/resources/spring.xml");
//获取的实例
User user=ctx.getBean("user");
3.3 面向切面编程(AOP)
3.3.1 简介
面向切面编程(Aspect Oriented Programming)提供了另一种角度来思考程序的结构,通过这种方式弥补面向对象编程(Object Oriented Programming)的不足。除了类以外,AOP提供了切面,切面对关注点进行模块化,例如横切多个类型和对象的事务管理(这些关注点术语通常称作横切(crosscutting)关注点)。Spring AOP是Spring的一个重要组件,但是Spring IOC并不依赖于Spring AOP,这意味着你可以自由选择是否使用AOP,AOP提供了强大的中间件解决方案,这使得Spring IOC更加完善。我们可以通过AOP来实现日志监听,事务管理,权限控制等等。
3.3.2 主要用途
动态代理
- JDK动态代理:
动态代理类:在程序运行时,运用反射机制动态创建而成。JDK的动态代理必须具备四个条件:1、目标接口 2、目标类 3、拦截器 4、代理类
- 编写拦截器
/**
* 拦截器
* 1、目标类导入进来
* 2、事物导入进来
* 3、invoke完成:开启事务、调用目标对象的方法、事务提交
*/
public class Interceptor implements InvocationHandler {
private Object target; // 目标类
private Transaction transaction;
public Interceptor(Object target, Transaction transaction) {
this.target = target;
this.transaction = transaction;
}
/**
* @param proxy 目标对象的代理类实例
* @param method 对应于在代理实例上调用接口方法的Method实例
* @param args 传入到代理实例上方法参数值的对象数组
* @return 方法的返回值,没有返回值是null
* @throws Throwable
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
String methodName = method.getName();
if ("savePerson".equals(methodName)
|| "deletePerson".equals(methodName)
|| "updatePerson".equals(methodName)) {
this.transaction.beginTransaction(); // 开启事务
method.invoke(target); // 调用目标方法
this.transaction.commit(); // 提交事务
} else {
method.invoke(target);
}
return null;
}
}
- 测试JDK动态代理
public class TestJDKProxy {
@Test
public void testSave(){
/**
* 1、创建一个目标对象
* 2、创建一个事务
* 3、创建一个拦截器
* 4、动态产生一个代理对象
*/
Object target = new PersonDaoImpl();
Transaction transaction = new Transaction();
Interceptor interceptor = new Interceptor(target, transaction);
/**
* 参数一:设置代码使用的类加载器,一般采用跟目标类相同的类加载器
* 参数二:设置代理类实现的接口,跟目标类使用相同的接口
* 参数三:设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
*/
PersonDao personDao = (PersonDao) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
interceptor);
personDao.savePerson();
}
}
- 总结:
1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
缺点:
1、在拦截器中除了能调用目标对象的目标方法以外,功能是比较单一的,在这个例子中只能处理事务
2、拦截器中的invoke方法的if判断语句在真实的开发环境下是不靠谱的,因为一旦方法很多if语句需要写很多。
动态代理
- CGLIB动态代理:
-
编写拦截器
public class Interceptor implements MethodInterceptor {
private Object target; // 代理的目标类 private Transaction transaction; public Interceptor(Object target, Transaction transaction) { this.target = target; this.transaction = transaction; } /** * 创建目标对象的代理对象 * * @return */ public Object createProxy() { // 代码增强 Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象 enhancer.setCallback(this); // 参数为拦截器 enhancer.setSuperclass(target.getClass());// 设置父类 return enhancer.create(); // 创建代理对象 } /** * @param obj 目标对象代理类的实例 * @param method 代理实例上 调用父类方法的Method实例 * @param args 传入到代理实例上方法参数值的对象数组 * @param methodProxy 使用它调用父类的方法 * @return * @throws Throwable */ public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { this.transaction.beginTransaction(); method.invoke(target); this.transaction.commit(); return null; }
}
- 测试CGLIB动态代理:
/**
* 测试cglib动态代理
* 通过cglib产生的代理对象,代理类是目标类的子类
*/
public class TestCglibProxy {
@Test
public void testSave(){
Object target = new PersonDaoImpl();
Transaction transaction = new Transaction();
Interceptor interceptor = new Interceptor(target, transaction);
PersonDaoImpl personDaoImpl = (PersonDaoImpl) interceptor.createProxy();
personDaoImpl.savePerson();
}
}
- 总结:
1、CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2、用CGlib生成代理类是目标类的子类。
3、用CGlib生成 代理类不需要接口。
4、用CGLib生成的代理类重写了父类的各个方法。
5、拦截器中的intercept方法内容正好就是代理类中的方法体
- CGLIB和JDK动态代理区别:
JDK:
目标类和代理类实现了共同的接口
拦截器必须实现InvocationHandler接口,而这个接口中invoke方法体的内容就是代理对象方法体的内容
CGLIB:
目标类 是代理类的父类
拦截器必须实现MethodInterceptor接口,而接口中的intercept方法就是代理类的方法体,使用字节码增强机制创建代理对象的.
4 spring 通知
4.1 通知种类
通知名称 | 阐述 |
---|---|
前置通知(Before advice) | 在某个连接点(Join point)之前执行的通知,但这个通知不能阻止连接点的执行(除非它抛出一个异常)。 |
返回后通知(After returning advice) | 在某个连接点(Join point)正常完成后执行的通知。例如,一个方法没有抛出任何异常正常返回。 |
抛出异常后通知(After throwing advice) | 在方法抛出异常后执行的通知。 |
后置通知(After(finally)advice) | 当某个连接点(Join point)退出的时候执行的通知(不论是正常返回还是发生异常退出)。 |
环绕通知(Around advice) | 包围一个连接点(Join point)的通知,如方法调用。这是最强大的一种通知类型。环绕通知可以在方法前后完成自定义的行为。它也会选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。 |
4.2 通知概要
通知对应的在动态代理中的位置:
try{
//前置通知
result = method();
//返回通知
}catch(Exception e){
e.printStackTrace();
//异常通知
}
//后置通知
注意切点表达式(注意xml文件中切点表达式的写法,&& 要使用and 代替):
4.3 基于xml文件配置通知
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"
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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="flowPush" class="com.yinhai.rsrc.common.utils.flowPushAop"/>
<aop:config proxy-target-class="true">
<!-- 配置切面 -->
<aop:aspect ref="flowPush">
<!-- 配置切点 -->
<aop:pointcut id="flowPushAspect"
expression="execution(* com.yinhai.rsrc.base.flow.service.impl.FlowPushServiceImpl.flow* (..))" />
<!-- 配置通知 -->
<aop:after-returning pointcut-ref="flowPushAspect" method="afterDeclare" returning="result" />
</aop:aspect>
</aop:config>
</beans>
4.4 基于注解配置通知
第一步是在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/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 激活组件扫描功能,在包com.spring.aop及其子包下面自动扫描通过注解配置的组件 -->
<context:component-scan base-package="com.spring.aop"/>
<!-- 激活自动代理功能 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
第二步是为Aspect切面类添加注解:
@Aspect //声明这是一个切面Bean
@Component //声明这是一个组件
public class MyAspect {
//配置前置通知及切点(其他通知只要改相关注解即可)
@Before("execution(* com.spring.aop.*(..))")
public void beforeMethod(JoinPoint joinpoint){
String methodName = joinpoint.getSignature().getName();
List<Object> list = Arrays.asList(joinpoint.getArgs());
System.out.println("方法:"+methodName+" 参数是"+list);
}
@AfterReturning(value = "execution(* calculator.CalculatorImpl..*(..))",returning = "result")
public void afterMethod(
JoinPoint joinpoint,Object result){
String methodName = joinpoint.getSignature().getName();
System.out.println("方法:"+methodName+" 返回值:"+result);
}
}
5. spring常用注解浅析
注解 | 解释 |
---|---|
@Controller | 组合注解(组合了@Component注解),应用在MVC层(控制层),DispatcherServlet会自动扫描注解了此注解的类,然后将web请求映射到注解了@RequestMapping的方法上。 |
@Service | 组合注解(组合了@Component注解),应用在service层(业务逻辑层) |
@Reponsitory | 组合注解(组合了@Component注解),应用在dao层(数据访问层) |
@Component | 表示一个带注释的类是一个“组件”,成为Spring管理的Bean。当使用基于注解的配置和类路径扫描时,这些类被视为自动检测的候选对象。同时@Component还是一个元注解。 |
@Autowired | Spring提供的工具(由Spring的依赖注入工具(BeanPostProcessor、BeanFactoryPostProcessor)自动注入。) |
@Resource | JSR-250提供的注解 |
@Configuration | 声明当前类是一个配置类(相当于一个Spring配置的xml文件) |
@Bean | 注解在方法上,声明当前方法的返回值为一个Bean。返回的Bean对应的类中可以定义init()方法和destroy()方法,然后在@Bean(initMethod=”init”,destroyMethod=”destroy”)定义,在构造之后执行init,在销毁之前执行destroy。 |
@Aspect | 声明一个切面(就是说这是一个额外功能) |
@After | 后置建言(advice),在原方法前执行。 |
@Before | 前置建言(advice),在原方法后执行。 |
@Around | 环绕建言(advice),在原方法执行前执行,在原方法执行后再执行(@Around可以实现其他两种advice) |
@PointCut | 声明切点,即定义拦截规则,确定有哪些方法会被切入 |
@Transactional | 声明事务(一般默认配置即可满足要求,当然也可以自定义) |
@Cacheable | 声明数据缓存 |
@Value | 值得注入。经常与Sping EL表达式语言一起使用,注入普通字符,系统属性,表达式运算结果,其他Bean的属性,文件内容,网址请求内容,配置文件属性值等等 |
@PropertySource | 指定文件地址。提供了一种方便的、声明性的机制,用于向Spring的环境添加PropertySource。与@configuration类一起使用。 |
@RunWith | 这个是Junit的注解,springboot集成了junit。一般在测试类里使用:@RunWith(SpringJUnit4ClassRunner.class) — SpringJUnit4ClassRunner在JUnit环境下提供Sprng TestContext Framework的功能 |
@ContextConfiguration | 用来加载配置ApplicationContext,其中classes属性用来加载配置类:@ContextConfiguration(classes = {TestConfig.class(自定义的一个配置类)}) |
@RequestMapping | 用来映射web请求(访问路径和参数),处理类和方法的。可以注解在类和方法上,注解在方法上的@RequestMapping路径会继承注解在类上的路径。同时支持Serlvet的request和response作为参数,也支持对request和response的媒体类型进行配置。其中有value(路径),produces(定义返回的媒体类型和字符集),method(指定请求方式)等属性。 |
@ResponseBody | 将返回值放在response体内。返回的是数据而不是页面 |
@RequestBody | 允许request的参数在request体中,而不是在直接链接在地址的后面。此注解放置在参数前。 |
@RestController | 组合注解,组合了@Controller和@ResponseBody,当我们只开发一个和页面交互数据的控制层的时候可以使用此注解。 |
6. springMVC
6.1 springMVC 简介
SpringMVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,SpringMVC也是要简化我们日常Web开发的。
6.2 核心组件及流程介绍
组件名称 | 描述 | 作用 |
---|---|---|
DispatcherServlet | 前端控制器,把请求给转发到具体的控制类 | 接收请求,响应结果,相当于转发器,中央处理器。有了DispatcherServlet减少了其它组件之间的耦合度。 |
View | 视图View | View是一个接口,实现类支持不同的View类型(jsp、freemarker、pdf…) |
HandlerMapping | 映射处理器,负责映射中央处理器转发给controller时的映射策略 | 根据请求的url查找Handler。 |
ViewResolver | 视图解析器,解析具体的视图 | 进行视图解析,根据逻辑视图名解析成真正的视图(view) |
HandlerAdapter | 处理器适配器,处理一些功能请求,返回一个ModelAndView对象 | 按照特定规则(HandlerAdapter要求的规则)去执行Handler。 |
流程介绍:
第一步:用户发送请求到前端控制器(DispatcherServlet)。
第二步:前端控制器请求 HandlerMapping 查找 Handler,可以根据 xml 配置、注解进行查找。
第三步:处理器映射器 HandlerMapping 向前端控制器返回 Handler
第四步:前端控制器调用处理器适配器去执行 Handler
第五步:处理器适配器执行 Handler
第六步:Handler 执行完成后给适配器返回 ModelAndView
第七步:处理器适配器向前端控制器返回 ModelAndView
ModelAndView 是SpringMVC 框架的一个底层对象,包括 Model 和 View
第八步:前端控制器请求试图解析器去进行视图解析
根据逻辑视图名来解析真正的视图。
第九步:试图解析器向前端控制器返回 view
第十步:前端控制器进行视图渲染
就是将模型数据(在 ModelAndView 对象中)填充到 request 域
第十一步:前端控制器向用户响应结果
6.3 配置前端控制器
在 web.xml 文件中进行如下配置:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>SpringMVC_01</display-name>
<!-- 配置前端控制器DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--springmvc.xml 是自己创建的SpringMVC全局配置文件,用contextConfigLocation作为参数名来加载
如果不配置 contextConfigLocation,那么默认加载的是/WEB-INF/servlet名称-servlet.xml,在这里也就是 springmvc-servlet.xml
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!--第一种配置:*.do,还可以写*.action等等,表示以.do结尾的或者以.action结尾的URL都由前端控制器DispatcherServlet来解析
第二种配置:/,所有访问的 URL 都由DispatcherServlet来解析,但是这里最好配置静态文件不由DispatcherServlet来解析
错误配置:/*,注意这里是不能这样配置的,应为如果这样写,最后转发到 jsp 页面的时候,仍然会由DispatcherServlet进行解析,
而这时候会找不到对应的Handler,从而报错!!!
-->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
6.4 配置视图解析器
spring-mvc.xml中配置:
<!-- 配置逻辑视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="suffix" value=".jsp" /> <!-- 配置视图解析器的后缀 -->
<property name="prefix" value="/WEB-INF/page/" /> <!-- 配置视图解析器的前缀 -->
</bean>
6.5 配置处理器映射器
在spring-mvc.xml中配置,声明相关的 bean 和实现即可:
<!-- 配置注解适配如@RequestMapping -->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"></bean>
6.6 配置处理器适配器
在spring-mvc.xml中配置,声明相关的 bean 和实现即可:
<!-- 配置注解@Controller识别-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"></bean>