spring优点
1. spring是一个免费的开源框架、容器 2. spring非侵入式.
使用spring,编写一些业务类的时候不需要继承spring特定的类,通过配置完成依赖注入后就可以使用,此时,spring就没有侵入到我业务类的代码里。侵入式让用户代码产生对框架的依赖,这些代码不能在框架外使用,不利于代码的复用。但侵入式可以使用户跟框架更好的结合,更容易更充分的利用框架提供的功能。非侵入式的代码则没有过多的依赖,可以很方便的迁移到其他地方。但是与用户代码互动的方式可能就比较复杂。
3. 控制反转.依赖注入ioc 面向切面aop 4. 对事物支持
spring是一个轻量级的控制反转ioc和面向对象的框架。
spring组成
spring由7个核心模块组成,核心模块是spring core ,其他模块作用于spring core之上,主要负责bean创建及管理。
- spring Core spring的核心模块, 是spring提供的基本功能,主要的组件时BeanFactory。BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性的规范于实际应用程序的代码分开。
- spring ORM 提供orm的对象关系工具,
- spring AOP spring面向切面。
- spring Context spring上下文,向spring提供上下文信息。
- spring DAO 简化jdbc操作。
- spring Web 于 Web 的应用程序提供了上下文
- spring Web MVC springMVC框架
拓展
spring、springboot、springcloud关系
- springboot是spring的一套快速开发脚手架。
- springcloud是基于springboot的。
- springboot是专注于快速开发单位微服务的个体,springcloud是专注于微服务治理的框架。
- springboot的集成方案已经选择好了,能不配置就不配置,而springcloud是基于springboot来实现的。
IOC
IOC(控制反转)是spring的核心,是一种设计思想,而DI(依赖注入)是IOC的一种实现方式,实现IOC的方式有很多种,DI只是其中的一种,把创建对象和对象之间的关系交给spring来管理
图1 之前的方式存在对象之间的耦合关系,一层调用一层。 图2 就是用户想要调用的时候只调用中间件,这样在调用的时候只需要调用中间件,想调用那个对象不会影响到其他的对象,降低了对象之间的耦合性。ioc容器就是中间件。
IOC容器是spring的核心内容,可以使用xml实现IOC,也可以使用注解的方式实现IOC 采用xml配置bean的时候,是将配置信息和业务实现分离的,而采用注解的配置bean是将二者合二为一。
IOC理念实践
注 : spring 需要导入commons-logging进行日志记录 . 我们利用maven , 他会自动下载对应的依赖项 .
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
**1. 编写一个hello实体类 **
public class Hello {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void show(){
System.out.println("Hello,"+ name );
}
}
**2. 编写我们的spring文件 , 这里我们命名为beans.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--bean就是java对象 , 由Spring创建和管理-->
<!--就相当于Hello hello = new Hello();现在把由用户new 创建对象,更改为 xml注入的方式,把创建hello对象交给spring管理,hello 对象的属性是由Spring容器设置的,这个过程就叫做控制反转-->
<bean id="hello" class="com.kuang.pojo.Hello">
<property name="name" value="Spring"/>
</bean>
</beans>
**3. 测试 **
@Test
public void test(){
//解析beans.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
//getBean : 参数即为spring配置文件中bean的id .
Hello hello = (Hello) context.getBean("hello");
hello.show();
}
DI依赖注入
**构造器注入 **
<!-- name指参数名 -->
<!-- constructor-arg:构造器注入,当调用getBean()方法的时候初始化,对象中的name属性已经赋值。 -->
<bean id="hello" class="com.kuang.pojo.Hello">
<constructor-arg name="name" value="Spring"/>
</bean>
**set注入(重点) ** 依赖:bean对象的创建依赖于容器 注入:bean对象中的所有属性由容器来注入
Student.java
package com.kuang.pojo;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Student {
private String name;
private String[] books;
private List<String> hobbys;
private String wife;
public void setName(String name) {
this.name = name;
}
public void setBooks(String[] books) {
this.books = books;
}
public void setHobbys(List<String> hobbys) {
this.hobbys = hobbys;
}
public void setWife(String wife) {
this.wife = wife;
}
<!--常量注入-->
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
</bean>
<!--数组注入-->
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="books">
<array>
<value>西游记</value>
<value>红楼梦</value>
<value>水浒传</value>
</array>
</property>
</bean>
<!--数组注入-->
<bean id="student" class="com.kuang.pojo.Student">
<property name="name" value="小明"/>
<property name="hobbys">
<list>
<value>听歌</value>
<value>看电影</value>
<value>爬山</value>
</list>
</property>
</bean>
**其他方式 **
Bean的作用域
bean的作用域常用的是两种,单例模式(singleton)和多例模式(prototype)。bean作用域默认是单例模式Singleton。那些由spring IOC管理的对象统称为bean。
singleton:当一个bean的作用域是单例模式的时候,spring ioc只会创建一个bean实例,所有请求bean,都是同一个bean实例。在spring ioc容器创建bean对象,就会创建bean对象,不管用不用,容器都已经创建了。
prototype:当一个bean的作用域是多例模式的时候,所有请求该bean实例的时候,spring ioc容器都会返回一个新的实例。spring ioc创建容器的时候,不会创建多例模式作用域的bean,当请求bean的时候,spring ioc容器才会创建。
注解使用@Scope来设置该bean的作用域。
单例模式(多线程的) 优点:共享一个实例,内存占用少,GC开销小,可以快速获取到bean。因为单例获取bean操作,除了第一次生成之外,其余都是从缓存里获取的,所以很快。 缺点:共享资源存在线程安全问题 多例模式(单线程的) 优点:不存在线程安全问题(静态共享资源除外:html,htm,css ,js ,jpg .txt ) 缺点:多个实例,内存占用多,GC开销大
bean的自动装配
springIOC容器DI依赖注入自动装配通常使用注解的方式进行自动装配,注解有两种形式,@Autowired和@Resource,@Autowired是按照类型进行装配,@Resource是按照名称进行装配。 #
@Autowired
1. 将User类中的set方法去除掉,使用@autowired注解
public class User {
@Autowired
private Cat cat;
@Autowired
private Dog dog;
private String str;
public Cat getCat() {
return cat;
}
public Dog getDog() {
return dog;
}
public String getStr() {
return str;
}
}
2. bean的配置文件
<context:annotation-config/>
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/>
3. 测试,成功输出结果!
public class MyTest {
@Test
public void testMethodAutowire() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.getCat().shout();
user.getDog().shout();
}
}
//如果允许对象为null,设置required = false,默认为true
@Autowired(required = false)
private Cat cat;
@Resource 如果有指定的name,就先按照byname方式去查找装配,如果不成功就会按照byType的方式自动装配,如果还是不成功就会报出异常。
实体类
public class User {
//如果允许对象为null,设置required = false,默认为true
@Resource(name = "cat2")
private Cat cat;
@Resource
private Dog dog;
private String str;
}
bean.xml
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
<bean id="cat2" class="com.kuang.pojo.Cat"/>
<bean id="user" class="com.kuang.pojo.User"/
测试:结果成功。
public class MyTest {
@Test
public void testMethodAutowire() {
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
User user = (User) context.getBean("user");
user.getCat().shout();
user.getDog().shout();
}
}
配置文件2:beans.xml , 删掉cat2
<bean id="dog" class="com.kuang.pojo.Dog"/>
<bean id="cat1" class="com.kuang.pojo.Cat"/>
实体类上只保留注解
@Resource
private Cat cat;
@Resource
private Dog dog;
@AutoWired和@Resource区别:建议使用@Resource @Autowired是spring提供的,autowired只能通过byTpye进行查找装配。如果无法辨别就会报出异常。当server的实现类注解上@Server,server就能找到实现类,可是当实现类存在两个或以上的时候autowired就不知道装配那个实现类了,就会报出异常,所以使用它就要确保一个server只有一个实现类 。resource是java提供的,是按照name进行查找装配,如果查找装配失败,就会按照type进行查找装配,如果还是没找到,就会报出异常。resource在使用spring框架的时候会减少耦合性。
衍生注解
@Component("user")组件
// 相当于配置文件中
<bean id="user" class="当前注解的类"/>
为了更好的分层,@Component有三个衍生注解
@Controller:web层
@Service:service层
@Repository:dao层
这些注解都是将所注解的类交给spring来管理
bean的作用域
使用注解@Scope可以修改bean的作用域,singleton(单例模式)是默认的。还可以修改为prototype(多例模式)。
spring 代理模式
spring的代理模式分为静态代理和动态代理两种模式。springAOP(面向切面)的底层机制就是动态代理。 #E91E63
静态代理
- 抽象角色:需要共同完成的事情,一般指的是接口或者抽象类。比如:房屋要出租
- 真实角色:就是被代理的角色,真实要求做事的角色:房东
- 代理角色:代理真实角色,代理真实角色后,一般会做一些附属操作:房屋中介
- 客户:使用代理模式来进行一些操作。
代码实现
//抽象角色:增删改查业务
public interface UserService {
void add();
void delete();
void update();
void query();
}
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
//代理角色,在这里面增加日志的实现
public class UserServiceProxy implements UserService {
private UserServiceImpl userService;
public void setUserService(UserServiceImpl userService) {
this.userService = userService;
}
public void add() {
log("add");
userService.add();
}
public void delete() {
log("delete");
userService.delete();
}
public void update() {
log("update");
userService.update();
}
public void query() {
log("query");
userService.query();
}
public void log(String msg){
System.out.println("执行了"+msg+"方法");
}
}
//测试实现
public class Client {
public static void main(String[] args) {
//真实业务
UserServiceImpl userService = new UserServiceImpl();
//代理类
UserServiceProxy proxy = new UserServiceProxy();
//使用代理类实现日志功能!
proxy.setUserService(userService);
proxy.add();
}
}
代理模式的好处:可以使真实角色操作更加纯粹(房东指出租房子)。不用去关心一些公共业务(看房子、签合同)等。 公共业务(看房子、签合同)交给代理对象(房屋中介)。实现了业务分工。 公共业务发生扩展的时候变得更加方便和集中(比如扩展业务:租金) 代理模式的缺点:增加了代理环节,就增加的工作量(房屋出租过程更加繁琐)
我们在不改变原来的代码的情况下,实现了对原有功能的增强,这是AOP中最核心的思想。我们想使用代理模式的好处,又不想要代理模式的缺点,就产生了动态代理 #FF9800
动态代理
动态代理分为两大类,基于接口(JDK动态代理)和基于类(cglib),JDK动态代理核心类 : InvocationHandler和Proxy,打开JDK帮助文档看看 #03A9F4
代码实现
//抽象角色:增删改查业务
public interface UserService {
void add();
void delete();
void update();
void query();
}
//真实对象,完成增删改查操作的人
public class UserServiceImpl implements UserService {
public void add() {
System.out.println("增加了一个用户");
}
public void delete() {
System.out.println("删除了一个用户");
}
public void update() {
System.out.println("更新了一个用户");
}
public void query() {
System.out.println("查询了一个用户");
}
}
public class ProxyInvocationHandler implements InvocationHandler {
//要代理的真实对象
private Object target;
public void setTarget(Object target) {
this.target = target;
}
//生成代理类,第一个参数指当前代理类,重点是第二个参数,获取要代理的抽象角色!
public Object getProxy(){
return Proxy.newProxyInstance(this.getClass().getClassLoader(),
target.getClass().getInterfaces(),this);
}
// proxy : 代理类
// method : 代理类的调用处理程序的方法对象.
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
//通过反射执行下面的UserService 的方法
Object result = method.invoke(target, args);
return result;
}
public void log(String methodName){
System.out.println("执行了"+methodName+"方法");
}
}
//测试
public class Test {
public static void main(String[] args) {
//真实对象
UserServiceImpl userService = new UserServiceImpl();
//代理对象的调用处理程序
ProxyInvocationHandler pih = new ProxyInvocationHandler();
pih.setTarget(userService); //设置要代理的对象
UserService proxy = (UserService)pih.getProxy(); //动态生成代理类!
proxy.delete();
}
}
静态代理需要我们在静态代理类中先声明需要代理的对象,而动态代理代理类不需要我们声明代理的真实对象,而是通过反射的机制执行真实对象方法是代理业务,真实对象调用的时候只需要在业务类中声明代理类就行了。
spring AOP
什么是aop:面向切面编程,利用AOP可以将业务的代码实现进行隔离,从而降低业务上的各部分耦合性,提高代码可重用性,提高代码开发的效率。aop把公共的业务(日志、事物、安全)进行管理,使开发人员只专注于代码实现。其本质还是动态代理。
使用注解实现
package com.kuang.config;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
@Component
public class AnnotationPointcut {
@Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void before(){
System.out.println("---------方法执行前---------");
}
@After("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void after(){
System.out.println("---------方法执行后---------");
}
@Around("execution(* com.kuang.service.UserServiceImpl.*(..))")
public void around(ProceedingJoinPoint jp) throws Throwable {
System.out.println("环绕前");
System.out.println("签名:"+jp.getSignature());
//执行目标方法proceed
Object proceed = jp.proceed();
System.out.println("环绕后");
System.out.println(proceed);
}
}
spring声明式事务
- 原子性:要么完成、要么不完成。
- 一致性:一旦所有事务动作完成,事务就要被提交。
- 隔离性:多个资源存在隔离,防止数据损坏。
- 持久性:事务一旦完成、不管系统发生什么问题,结果都不会影响,被持久化的写道数据库。