简介
作为一个Java的大家族,Spring是Java开发中应用最广泛最成熟的框架。在Spring的官方文档中,Spring的组件包括:
1.Spring IOC(控制反转)
Ioc (Inversion of Control),即“控制反转”它不是什么技术,而是一个思想。IOC意味着你将设计好的对象交给容器来控制,而不是像以前那样在对象的内部进行控制,举个简单的例子:
当有一个类A,一个类B,A中有一个B类型的成员变量b:
class A{
B b;
//以前的内部控制 b=new B()
//在Spring IOC的加持下 将控制权交给核心容器 开发人员只需要在需要该变量时由核心容器自己装配
}
class B{}
理解IOC就必须要理解下面的两个点:
-
谁控制谁,控制什么?
传统的JavaSE中创建对象是通过在对象内部使用new关键字来进行创建的。是程序主动去创建依赖对象;而IOC中有一个专门的容器来创建这些对象,即由IOC容器来控制对象的创建;
-
为何是反转?哪些方面反转了?
正转 | 传统应用程序是由我们自己咋对象中主动控制去直接获取依赖对象 |
---|---|
反转 | 由容器帮助我们查找以及注入依赖对象,对象只是被动的接收依赖对象 |
2.Spring DI(依赖注入)
DI(Dependency Injection),即“依赖注入”,它也只是一种思想,是IOC从另一个方向出发看到的不同角度的东西。DI过程中,由容器动态的将某个依赖关系注入到组件中。依赖注入的目的并非为软件带能更多的功能,而是为了提升组件重用的频率,为系统搭建一个灵活可扩展的平台。
IOC的一个重点是在系统运行的过程中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI来实现注入的
实现DI的方式分为两种(其实有三种 spring自带的注解注入 但是这样就入侵了代码):
① 通过构造方法注入
package x.y;
public class Foo {
Bar bar;
Baz baz;
public Foo(Bar bar, Baz baz) {
this.bar=bar;
this.baz=baz;
}
}
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>
②通过setter方法注入
public class ExampleBean {
private AnotherBean beanOne;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<property name="beanOne">
<ref bean="anotherExampleBean"/>
</property>
<property name="integerProperty" value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
③注解自动装配
public class MovieRecommender {
@Autowired
private ApplicationContext context;
public MovieRecommender() {
}
// ...
}
3.bean的生命周期
1.bean对象的实例化;
2.封装属性,也就是设置properties的内容;
3.如果bean实现了BeanNameAware,则执行setBeanName方法;
4.如果实现BeanFactoryAware或者ApplicationContextAware ,需要设置setBeanFactory或者上下文对象setApplicationContext;
5.如果存在类实现BeanPostProcessor后处理bean,执行postProcessBeforeInitialization,可以在初始化之前执行一些方法;
6.如果bean实现了InitializingBean,则执行afterPropertiesSet,执行属性设置之后的操作
7.调用< bean init-method="">执行指定的初始化方法
8.如果存在类实现BeanPostProcessor则执行postProcessAfterInitialization,执行初始化之后的操作
9.执行自身的业务方法
10.如果bean实现了DisposableBean,则执行spring的的销毁方法
11.调用执行自定义的销毁方法
4.Spring AOP(面向切面编程)
Spring AOP是Spring框架的一个核心技术,它旨在面向切面编程。AOP采用了一种叫做“横切”的技术,将涉及过多的业务流程的通用功能抽取出来并进行单独封装,形成独立的切面,在合适的时机将这些切面切入到业务流程的指定位置;
举个栗子: 当我们去医院看病的时候,一般的流程是从 挂号 -> 找医生 -> 诊断 -> 治疗 -> 吃药 -> 出院,在这个过程中,我们需要关心的是病人从生病到康复这个结果,其中的有些步骤(比如:挂号,诊断,治疗)是所有的病人都得做的工作,所以AOP就可以将这些重复但和主业务逻辑无关的重要步骤抽取出来当作切面,在合适的时间将这些业务重新织入到主业务上,完成一套程序的整个流程;
AOP中的相关概念
- Aspect(切面)
通常是一个类,是切入点pointcut和通知advice的集合;
@org.aspectj.lang.annotation.Aspect
//观众类
public class Audience {
//...
}
- Join point(连接点)
程序执行过程中的一点,例如方法的执行或异常的处理;
@Component
public class MovieStar implements Performence {
public void performence(int num) throws Exception {
System.out.println("Apach code表演");
if(num==3){
throw new RuntimeException();
}
}
}
- Advice(通知)
在特定的连接点出采取的操作。不同类型的通知包括before、after、around;
@Before("proference()") 执行方法之前
@AfterReturning("proference()") 方法返回之后
@AfterThrowing("proference()") 方法抛出异常之后
@Around("proference()") 环绕通知
- Point cut(切点)
与连接点匹配,主要用于书写被增强的连接点;
@Pointcut("execution(* Performence.performence(..))") 执行所有有关performence通知方法
- Target object(目标对象)
一个或多个方面建议的对象,即需要被代理的对象; - AOP proxy(AOP代理)
由AOP框架创建一个对象,AOP中的代理分为动态代理和Cglib; - Weaving(织入)
把增强的advice应用到目标对象target来创建新的代理对象proxy的过程;
动态代理和CGLIB
先表明需要代理的类:
interface User{
public void addUser(String name,String passwd);
}
public class UserImp implements User{
String name;
String passwd;
public void addUser(String name,String passwd){
this.name=name;
this.passwd=passwd;
}
}
- JDK动态代理
public class JDKProxy implements InvocationHandler{
private Object target; //需要代理的目标
//重写invoke方法
@Override
public Object invoke(Object proxy,Method method,Object[]args) throw Throwable{
Object result=method.invoke(target,args);
}
//JDK动态代理只能针对实现了接口的类进行代理
//传入被代理类的类加载器 被代理类的对象数组 被代理类的方法调用处理器
private Object getJDKProxy(Object targetObj){
this.target=targetObj;
return Proxy.newProxyInstance(targetObj.getClass().getClassLoader(),
targetObject.getClass().getInstance(),
this);
}
public static void main(String[]args){
JDKProxy proxy=new JDKProxy();
User user=(User)proxy.getJDKProxy(new UserImp());//传入被代理类User 获取动态代理
user.addUser("Chinese","hongkong belongs to China");
}
}
- CGLIB
作为一个强大的高性能的Code库,CGLIB可以在运行期扩展Java类和实现Java接口。CGLIB的强大之处在于它不仅可以实现接口,还可以扩展类,解决了有的类没有实现接口而无法被代理的尴尬
在使用CGLIB之前需要导入两个jar包:asm-5.2.jar cglib-3.2.5.jar
public class CglibProxy implements MethodInterceptor{
private Object target; //被代理类
//重写拦截方法
@Override
public Object intercept(Object obj,Method method,Object[]arr,MethodProxy proxy) throw Throwable{
Object invoke=method.invoke(target,arr); //目标对象 + 参数数组
return invoke;
}
public Object getCglibProxy(Object objectTarget){
this.target=objectTarget; //为目标类赋值
Enhancer enhance=new Enhancer();
//设置父类 因为Cglib是针对指定的类生成一个子类
enhancer.setSuperclass(ObjectTarget.getClass());
enhancer.setCallBack(this); //设置回调
Object result=enhancer.create();//创建并返回代理对象
return result;
}
public static void main(String[]args){
CglibProxy proxy=new CglibProxy();
User user=(User)proxy.getCglibProxy(new UserImp());
//...代理方法
user.addUser("Chinese","Xizang belongs to China");
}
}
JDK动态代理和CGLIB的比较
JDK动态代理 | CGLIB | |
---|---|---|
基本概念 | JDK动态代理主要涉及到java.lang.reflect包下面的两个类:Proxy和InvocationHandler | 采用字节码技术,在子类中利用方法拦截所有父类的方法并顺势织入到横切逻辑 |
实现原理 | 通过实现InvocationHandler接口创建自己的调用处理器;通过为Proxy类指定ClassLoader对象和一组interface数组来创建动态代理;通过反射机制获取动态代理类的构造函数;通过构造器创建动态代理类的实例 | 在运行期动态扩展java类,重写父类的方法,实现AOP切面编程 |
对比 | JDK动态代理是面向接口编程的 | CGLIB是通过底层字节码继承代理类来实现的(如果代理类被final修饰,那么CGLIB就会失败) |
性能 | JDKProxy创建对象快 | CGLIB实际运行速度快 |
5.Spring MVC
SpringMVC是一种基于Java实现的MVC设计模式的请求驱动类型的轻量级web框架,使用MVC架构模式的思想,将web层进行职责解耦,基于请求驱动就是使用请求-响应模型,框架的目的就是帮助我们简化开发;
谈一谈MVC模式
MVC(Model - View - Controller) ,模型 -视图-控制器模式,这种模式适用于应用程序的分层开发;
- Model(模型) - 代表一个存取数据的Java POJO。他可以带有逻辑,在数据变化时更新控制器;
- View(视图) - 代表模型包含的数据可视化;
- Controller(控制器) - 作用于模型和视图层。它控制数据流向模型对象,并在数据变化时更新视图。
创建模型:
public class Student{
private String name;
private int sid;
public String getName(){
retur name;
}
public void setName(String name){
this.name=name;
}
public int getSid(){
return sid;
}
public void setSid(int sid){
this.sid=sid;
}
}
创建视图:
public class StudentView{
public void printStudentDetail(String name,int sid){
System.out.println("name : "+name +", sid : "+sid);
}
}
创建控制器:
public class StudentController{
private Student model;
private StudentView view;
public StudentController(Student model,StudentView view){
this.model=model;
this.view=view;
}
public void setStudentName(String name){
model.setName(name);
}
public String getStudentName(){
return model.getName();
}
public void setStudentId(int sid){
model.setSid(sid);
}
public int getStudentSid(){
return model.getSid();
}
public void updateView(){
view.printStudentDetial(getStudentName(),getStudentSid());
}
}
根据StudentController来演示MVC设计模式的用法:
public class MVCPatternDemo{
public static void main(String[]args){
//1.从数据库获取到学生信息;
Student model=retrieveStudentFromDataBase();
//2.创建一个视图,把学生信息输出到控制台
StudentView view=new StudentView();
StudentController controller=new StudentController(model,view);
controller.updateView();
//3.更新数据
controller.setStudentName("Lisa");
controller.setStudentSid(1);
controller.updateView();
}
privtate static Student retrieveStudentFromDataBase(){
Student student = new Student();
student.setName("Bob");
student.setSid(2);
}
}
在web MVC应用开发中,Spring MVC框架是围绕着DispatcherServlet来设计的,该框架将请求分配给处理程序,并且具有可配置的处理程序映射,视图分辨率,区域设计,时区和主题分辨率,以及对上传文件的支持。
Spring MVC中请求处理工作流程
具体过程:
1.用户向服务端发送一次请求,这个请求会先到中央控制器(DistpatcherServlet);
2.DispatcherServlet收到后会调用HandlerMapping处理器映射器。由此可以得知请求该通过哪个
Controller来处理;
3.DispatcherServlet调用HandlerAdapter处理器适配器,告诉处理器去执行哪个Controller;
4.HandlerAdapter处理器适配器去执行Controller并得到ModelAndView,并层层返回给DispartchServlet;
5.DispartcherServlet将ModelAndView交给视图解析器ViewReslover,然后返回真正的视图;
6.DispatcherServlet将视图模型填充到视图中,并将结果响应到用户;
Spring MVC并发访问的安全问题
Spring MVC的Controller默认为单例模式,一般的Controller,service,DAO层都是singleton。所以会产生一个潜在的安全隐患,根本核心是instance变量保持状态的问题。这意味着每一个request过来都要用原有的instance去处理,这样就导致了两个结果:
- 我们不用每次创建Controller
- 减少了对象的创建时间和垃圾回收
由于只有一个Controller的instance,当多个线程同时调用的时候,里面的instance就不是线程安全的了,会发生数据篡改的情况。比如:
public class Controller extends AbstarctCommandController{
protected ModelAndView handle(HttpServletRequest requ,HttpServletResponse resp,
Object command,BindException errors) throws Execption{
company = new Company();
}
protected Company company;
}
这里就存在多线程并发时安全问题:
company为公共的,若是某个请求修改了company,则别的请求能够读取修改的内容;
解决方案:
- 在控制器中不使用实例变量;
- 将单利模式修改为多例模式 ;
在Spring 的Controller中声明scope = “prototype”
- 在Controller层使用ThreadLoacl本地变量;
6.Spring Boot
Spring Boot提供了一种构建应用程序的快速方法。 它查看您的类路径和配置的bean,对丢失的内容做出合理的假设,然后添加。 借助Spring Boot,您可以将更多精力放在业务功能上,而不必在基础架构上。要知道,SpringBoot号称15分钟就可以搭建一个Web项目!
这里放一个Boot的官方地址 : https://spring.io/guides/gs/spring-boot/
优点 | 缺点 |
---|---|
可独立运行Spring项目 | 没有提供相应的服务发现和注册的配套功能 |
内嵌Tomcat容器 | 离真正的微服务还有一定的距离 |
提供starter简化maven配置 | |
自动配置Spring | |
无代码生成和XML配置 |
传统基于Spring的Java Web应用,需要配置web.xml, applicationContext.xml,将应用打成war包放入应用服务器(Tomcat, Jetty等)中并运行。如果基于Spring Boot,这一切都将变得简单:
以Maven为例,程序员的第一个程序都是HelloWorld :
- 先导入SpringBoot的相关依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependencies>
- 编写一个类包含处理HTTP请求的方法和一个main()函数:
@Controller
@EnableAutoConfiguration
public class SampleController {
@RequestMapping("/")
@ResponseBody
String home() {
return "Hello World!";
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleController.class, args);
}
启动该main方法后,在浏览器中输入http://localhost:8080
,HelloWorld就会显示!(注意:开发Web应用程序推荐使用Intelij IDEA)
@EnableAutoConfiguration : 打开自动配置的功能,也可以关闭某个自动配置的选项;
@ComponentScan : Spring 组件扫描
如何理解Spring Boot中的starters?
Starts可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式的集成Spring及其他技术,而不需要找到代码和依赖包。
如何在Spring Boot启动的时候运行一些特定的代码
可以实现接口ApplicationRunner或者CommandLineRunner,这两个接口的实现方式是一样的,他们都只提供了一个run方法;
Spring Boot中的热部署
主要有两种方式:
- Spring Loader
- Spring-boot-devtools
引入依赖包 true表示支持热部署
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>