简介
行业春天
解决企业级开发的复杂性,使现有的技术更容易使用,本身是个大杂烩,整合了现有的技术框架
不要重复造轮子
- SSH:Struct2+Spring+Hibernate
- SSM:SpringMVC+Spring+Mybatis
官网:https://spring.io/projects/spring-framework#overiew
官方下载地址:https://repo.spring.io/release/org/springframework/spring/
可以下载到所有的版本
GitHub:https://github.com/spring-projects/spring-framework
SpringWebMVC依赖文本
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.7.RELEASE</version>
</dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.7.RELEASE</version>
</dependency>
两个包:
- springWebMVC
- springJDBC
Spring优点
- Spring是一个开源的免费的框架(容器)
- Spring是一个轻量级,非入侵式框架!
- 不会对原来项目有影响
- 控制反转(IOC),面向切面编程(AOP)
- 支持事务处理,对框架整合的支持
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架
Spring组成
spring组成的七大模块
- 核心容器(SpringCore)
- 核心容器提供Spring框架的基本功能。spring以bean的方式组织和管理Java应用的各个组件及其关系,spring使用BeanFactory来产生和管理Bean,是工厂模式的实现,BeanFactory使用控制反转(IoC)模式将应用的配置和依赖性规范与实际的应用程序代码分开
- 应用上下文(Spring Context)
- Spring上下文是一个配置文件,向spring提供上下文信息,spring上下文包括企业服务、、、、
- Spring面向切面编程(Spring AOP)
- AOP(Aspect Oriented Programming)
- 通过配置管理特性,SpringAOP模块直接将面向方法的编程功能集成在了Spring框架中,Spring管理的任何对象都支持AOP,SpringAOP模块基于Spring的应用程序中的对象提供了事务管理服务,通过使用SpringAOP,不用依赖EJB组件,就可以将声明性事务管理集成在应用程序中
- JDBC和DAO模块(Spring DAO)
- Dao(Data Access Object)
- JDBC、DAO的抽象层,提供了有意义的异常层次结构实现,可用该结构来管理异常处理,和不同数据库提供商抛出的错误信息,异常层次结构简化了错误处理,并且极大的降低了需要编写的代码数量,比如打开和关闭链接。
- 对象实体映射(Spring ORM)
- ORM(Object Relational Mapping)
- Spring插入了若干个ORM框架,提供了ORM对象的关系工具,其中包括Hibernate,JDO和IBatisSQL Map等,所有这些都遵从Spring的通用事务和DAO异常层次结构
- Web模块(Spring Web)
- web上下文模块建立应用程序上下文模块之上,基于web的应用程序提供了上下文,所以spring框架支持与Struts集成,web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作
- MVC模块(SpringWebMVC)
- MVC(Model View Controller)
- MVC框架是一个全功能的构建Web应用程序的MVC实现,通过策略接口,MVC框架编程高度可配置的,MVC容纳了大量视图技术,其中包括JSP,POI等,模型由JavaBean来构成,存放于m当中,而视图是一个接口,负责实现模型,控制器表示逻辑代码,由c的事情。spring框架的功能可以用在任何J2EE服务器当中,大多数功能也适用于不受管理的环境,spring的核心要点就是支持不绑定到特定J2EE服务的可重用业务和数据的访问对象,毫无疑问这样的对象可以在不同的J2EE环境,独立应用程序和测试环境之间重用
Spring介绍
- SpringBoot
- 快速开发脚手架
- 可以快速开发单个微服务,把一个功能单独提取出来就是一个微服务
- 约定大于配置,主要学习他的配置
- 学习SpringBoot的前提是要掌握Spring和SpringMVC
- SpringCloud
- SpringCloud基于SpringBoot实现的
之前使用的流程:
- UserDao接口
- UserDaoImpl实现类
- UserService业务接口
- UserServiceImpl业务实现类
在之前的业务中,用户的需求会影响到我们原来的代码,需要根据用户需求去修改源代码,如果程序代码量巨大,修改一次的成本十分昂贵
使用set接口来实现也是一种动态实现 值的注入
- 之前,程序是主动创建对象,控制权在程序员手上
- 使用了set注入后,程序不再具有主动性,而是变成了被动的接受对象
这种思想,本质上解决了问题,程序员不用再去管理对象的创建,系统的耦合性大大的降低,可以更加专注的在业务的实现上,这是IOC的原型
以前的架构
现在的架构
IOC本质
- 是一种设计思想,DI(依赖注入)是实现IOC的一种方法,原先创建对象由程序自己控制,控制反转后,创建转移给第三方,获得依赖对象的方式反转了
- IoC是Spring框架的核心内容
- 可以使用XML配置
- 可以使用注解
- 不配置也行,自动装填
- 控制反转时一种通过描述(XML或注解)并通过第三方生产或获取特定对象的方式,在Spring中实现控制反转的时IoC容器,其实现方法是依赖注入(Dependency Injection ,DI)
制作一个简单演示,如何将new对象交给第三方来做,用户可以通过修改xml文件来实现IoC操作
- 导入包
- 配置类加载路径应用上下文xml文件
- 编写JavaBean
- 测试,通过上下文获取bean来new对象
<dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.7.RELEASE</version>
</dependency>
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="..." class="..."> <!-- collaborators and configuration for this bean go here --></bean><bean id="..." class="..."><!-- collaborators and configuration for this bean go here --></bean><!-- more bean definitions go here --></beans>
@Data
public class Hello {private String str;
}
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="Hello" class="com.haoyun.POJO.Hello"><property name="str" value="haoyun"/></bean>
</beans>
public class MyTest {@Testpublic void BeanTest(){ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("Beans.xml");Hello hello = (Hello) classPathXmlApplicationContext.getBean("Hello");System.out.println(hello.toString());}
}
IOC创建对象的方式
@Data
public class Hello {private String name;public Hello(String name){}
}
<bean id="Hello" class="com.haoyun.POJO.Hello"><property name="name" value="sdafsdf"/>
</bean>
依赖注入的两种变体
DI依赖注入存在两种主要变体:Constructor 和Setter 的两种依赖注入
Constructor
<beans><bean id="thingOne" class="x.y.ThingOne"><constructor-arg ref="thingTwo"/><constructor-arg ref="thingThree"/></bean><bean id="thingTwo" class="x.y.ThingTwo"/><bean id="thingThree" class="x.y.ThingThree"/>
</beans>
<bean id="exampleBean" class="examples.ExampleBean"><constructor-arg type="int" value="7500000"/><constructor-arg type="java.lang.String" value="42"/>
</bean>
<bean id="exampleBean" class="examples.ExampleBean"><constructor-arg index="0" value="7500000"/><constructor-arg index="1" value="42"/>
</bean>
<bean id="exampleBean" class="examples.ExampleBean"><constructor-arg name="years" value="7500000"/><constructor-arg name="ultimateAnswer" value="42"/>
</bean>
Setter
调用no-argument构造函数,或static工厂方法来实例化Bean
ApplicationContext管理Beans的Constructor和setter的DI,
可以使用properties的格式,但是一般使用XML,或者注解@Controller等
Constructor和Setter是可以混合使用的
Setter注入应该要有合理的默认值,否则,应该添加not-null检查
- 可以由xml、JavaCode、annotations指定
- 每个property(属性)或构造函数参数都要设置value的实际定义,或是容器的另一个引用,如参数不是基本类型,就要找到容器中对应bean的reference(引用)
循环依赖
- classA通过构造函数实注入实现classB,classB又通过构造函数注入实现classA,这种配置bean的相互注入,会被SpringIoC判定为循环reference,并抛出BeanCurrentlyInCreationException
- 要求beanA和beanB之间必须有一个被完全初始化,再注入另一个,典型的先有鸡还是先有蛋的问题
Setter-Based example
public class ExampleBean {private AnotherBean beanOne;private YetAnotherBean beanTwo;private int i;public void setBeanOne(AnotherBean beanOne) {this.beanOne = beanOne;}public void setBeanTwo(YetAnotherBean beanTwo) {this.beanTwo = beanTwo;}public void setIntegerProperty(int i) {this.i = i;}
}
<bean id="exampleBean" class="examples.ExampleBean"><!-- setter injection using the nested ref element --><property name="beanOne"><ref bean="anotherExampleBean"/></property><!-- setter injection using the neater ref attribute --><property name="beanTwo" ref="yetAnotherBean"/><property name="integerProperty" value="1"/>
</bean><bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
<bean id="exampleBean" class="examples.ExampleBean"><!-- constructor injection using the nested ref element --><constructor-arg><ref bean="anotherExampleBean"/></constructor-arg><!-- constructor injection using the neater ref attribute --><constructor-arg ref="yetAnotherBean"/><constructor-arg type="int" value="1"/>
</bean><bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
最简单就直接
<bean id="Hello" class="com.haoyun.POJO.Hello"><constructor-arg name="name" value="haoyun"/>
</bean>
真是没事找事
别名alias
<bean id="Hello" class="com.haoyun.POJO.Hello"><constructor-arg name="name" value="haoyun"/>
</bean>
<alias name="Hello" alias="safsdf"/>
原来的名字也能用,别名也能用
这个又是没用的,直接再bean标签使用name也能起别名,里面的内容,用逗号或分号或空格都能起别名
<bean id="Hello" class="com.haoyun.POJO.Hello" name="hello afsdfaf,sadfasdf">
import配置文件
之前官网说一个正规的beans命名为applicationContext.xml,这是一个总的配置文件,项目中可以有多个配置文件,这样的好处是,可以由多个人编写,最后汇总,汇总到主配置文件applicationContext.xml
ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml","Beans.xml");
创建ApplicationContext实例的时候可以选用多个配置文件
或者只选择applicationContext.xml配置文件,将其他的配置文件导入applicationContext.xml
<import resource="Beans.xml"/>
<import resource="Beans1.xml"/>
<import resource="Beans2.xml"/>
多个Beans中配置相同的内容是没关系的
DI依赖注入几种方式注入,Constructor注入,setter注入,拓展方式注入
Set方式注入
- 依赖注入:set注入
- 依赖:Bean对象的创建依赖于容器
- 注入:Bean对象中的说有属性,由容器来注入
搭建环境
- 复杂类型
- 真实测试对象
直接把官网的举例的所有类型一次做出来
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><!-- <import resource="Beans.xml"/>--><!-- <import resource="Beans1.xml"/>--><!-- <import resource="Beans2.xml"/>--><bean id="address" class="com.haoyun.POJO.Address"/><bean id="student" class="com.haoyun.POJO.Student"><!--普通值注入,value--><property name="name" value="asdfafd"/><!--Bean注入,ref--><property name="address" ref="address"/><!--数组注入--><property name="books"><array><value>asdfsdf</value><value>asdfasd</value><value>sdfasdfa</value><value>asdfasdfqwer</value></array></property><!--list--><property name="hobbys"><list><value>adf</value><value>wer</value><value>trey</value></list></property><!--map--><property name="card"><map><entry key="asdf" value="asdfsf"/><entry key="asdfsdf" value="asdfsf"/><entry key="asddsaff" value="asdfsf"/></map></property><!--set--><property name="games"><set><value>sadf</value><value>ssadf</value><value>sdadf</value></set></property><!--null--><property name="wife"><null/></property><!--properties--><property name="info"><props><prop key="sadf">werqwer</prop><prop key="sasdf">werqwer</prop><prop key="saddf">werqwer</prop></props></property></bean></beans>
- value
- ref
- list、map、set、properties
- null
- 其他类型
p-namespace、c-namespace
命名空间,这两个能进行的操作其他的操作也能实现,还不一定简便了,要先添加配置支持扩展使用命名空间
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
添加在applicationContext.xml上,才可以使用,p和c标签
p表示property,用于有无参构造函数的bean对象的依赖注入操作
c表示Constructor,用于有 有参构造函数的bean对象的依赖注入操作
<!--c:constructor 需要有参构造器-->
<bean id="User" class="com.haoyun.POJO.User" c:age="12" c:name="asdfsf"/><!--p:property 需要无参构造器-->
<bean id="User2" class="com.haoyun.POJO.User" p:age="13" p:name="sdf"/>
测试
@Test
public void BeanTest(){ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");//Student student = (Student) classPathXmlApplicationContext.getBean("student");User user = (User) classPathXmlApplicationContext.getBean("User");User user2 = (User) classPathXmlApplicationContext.getBean("User2");System.out.println(user.toString());System.out.println(user2.toString());}
Bean-Scopes作用域
- Singleton 单例
- Prototype 原型
- request 请求
- session 会话
- application 应用
- WebSocket 连接
主要使用前两种
Singleton 单例模式
单例模式是Spring的默认机制
只管理Singleton bean 的一个共享实例,带有一个或多个match bean定义的id到导致只返回一个特定的bean实例
定义的bean限定为singleton时,只创建该bean定义的object的一个实例,存储在singleton缓存中,后续的请求都引用缓存中的实例
只创建一个实例
这个共享实例被共享到每个协作对象中
Spring中的singleton bean概念于singleton pettern的概念不同,如果在单个 Spring 容器中为特定 class 定义一个 bean,则 Spring 容器将创建该_ bean 定义所定义的 class 的一个且仅一个实例。
<bean id="accountService" class="com.something.DefaultAccountService"/><!-- the following is equivalent, though redundant (singleton scope is the default) -->
<bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
Demo
<bean id="studentTest" class="com.haoyun.POJO.Student" ><property name="address" ref="address"/>
</bean>
<bean id="studentTest2" class="com.haoyun.POJO.Student" ><property name="address" ref="address"/>
</bean>
<bean id="address" class="com.haoyun.POJO.Address" p:address="address" scope="singleton" />
@Test
public void BeanTest(){ApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");Student studentTest = (Student) classPathXmlApplicationContext.getBean("studentTest");Student studentTest2 = (Student) classPathXmlApplicationContext.getBean("studentTest2");System.out.println(studentTest);System.out.println(studentTest2);System.out.println(studentTest == studentTest2);System.out.println(studentTest.getAddress() == studentTest2.getAddress());
}
ProtoType 原型模式
bean部署的不是单例模式的原型范围都会创建一个新的bean实例,并对该特定bean发出请求,
每个bean都是一个单独的对象
配置和组装object交给client,client code必须清理prototype-scoped object释放原型beans所拥有的昂贵资源,之后可以尝试自定义bean post-processor,释放资源
<bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
每次从容器中get的时候都会产生一个新的对象
其余的request,session,application,这些只能在web开发中使用到,自行理解
Bean的自动装配- 自动装配,spring满足bean依赖的一种方式
- autowire
- spring会在上下文中自动寻找,并自动给bean装配属性
在spring中有三种装配方式
- 在xml中显示的配置
- 在java中显示的配置
- 隐式 的自动装配bean【重要】
XML Configuration Demo
<bean id="cat" class="com.haoyun.POJO.Cat"/>
<bean id="dog" class="com.haoyun.POJO.Dog"/><bean id="People" class="com.haoyun.POJO.People"><property name="name" value="haoyun"/><property name="cat" ref="cat"/><property name="dog" ref="dog"/>
</bean>
@Test
public void CatDogTest(){ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");People people = (People) classPathXmlApplicationContext.getBean("People");people.getCat().shout();people.getDog().shout();classPathXmlApplicationContext.registerShutdownHook();
}
<bean id="cat" class="com.haoyun.POJO.Cat"/><bean id="dog" class="com.haoyun.POJO.Dog"/>
<!-- <bean id="dog1" class="com.haoyun.POJO.Dog"/>如果这里的id更改,就找不到了--><!--byName:会自动再容器上下文查找,和自己对象set方法后面值对应的bean id--><bean id="People" class="com.haoyun.POJO.People" autowire="byName"><property name="name" value="haoyun"/></bean>
<bean id="cat" class="com.haoyun.POJO.Cat"/><bean class="com.haoyun.POJO.Dog"/>
<!-- <bean id="dog" class="com.haoyun.POJO.Dog"/> 但是不能配重,全局只能有一个--><!--连bean-id都可以不用--><!--byType:自动在容器的上下文查找,和自己对象相同类型bean--><bean id="People" class="com.haoyun.POJO.People" autowire="byType"><property name="name" value="haoyun"/></bean>
Annotation-based Container Configuration
使用annotation配置更加简洁,Spring都支持这两种配置风格
spring2.0引用了@Required
spring2.5采用通用的方法来驱动spring依赖注入@Autowired
2.5还添加了JSR-250注释的支持,@PostConstruct和@PreDestroy
(初始化方法)(销毁方法)
需要注册单独的Bean定义,和context命名空间
<?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"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/></beans>
主要是添加这三句话
@Autowired
public class MovieRecommender {private final CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao = customerPreferenceDao;}// ...
}
public class SimpleMovieLister {private MovieFinder movieFinder;@Autowiredpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// ...
}
public class MovieRecommender {private MovieCatalog movieCatalog;private CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic void (MovieCatalog movieCatalog,CustomerPreferenceDao customerPreferenceDao) {this.movieCatalog = movieCatalog;this.customerPreferenceDao = customerPreferenceDao;}// ...
}
public class MovieRecommender {private final CustomerPreferenceDao customerPreferenceDao;@Autowiredprivate MovieCatalog movieCatalog;@Autowiredpublic MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {this.customerPreferenceDao = customerPreferenceDao;}// ...
}
<context:annotation-config/><bean id="cat" class="com.haoyun.POJO.Cat"/>
<bean id="dog" class="com.haoyun.POJO.Dog"/><bean id="People" class="com.haoyun.POJO.People" p:name="asdfadf"/>
public class MovieRecommender {@Autowiredprivate MovieCatalog[] movieCatalogs;// ...
}
public class MovieRecommender {private Set<MovieCatalog> movieCatalogs;@Autowiredpublic void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {this.movieCatalogs = movieCatalogs;}// ...
}
public class MovieRecommender {private Map<String, MovieCatalog> movieCatalogs;@Autowiredpublic void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {this.movieCatalogs = movieCatalogs;}// ...
}
public class SimpleMovieLister {private MovieFinder movieFinder;@Autowired(required = false)public void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// ...
}
public class SimpleMovieLister {@Autowiredpublic void setMovieFinder(@Nullable MovieFinder movieFinder) {...}
}
public class MovieRecommender {@Autowired@Qualifier("main")private MovieCatalog movieCatalog;// ...
}
<bean class="com.haoyun.POJO.Cat"/><bean class="com.haoyun.POJO.Dog"/><bean id="People" class="com.haoyun.POJO.People" p:name="asdfadf"/>
<bean id="cat1" class="com.haoyun.POJO.Cat"/>
<bean id="cat2" class="com.haoyun.POJO.Cat"/><bean id="dog1" class="com.haoyun.POJO.Dog"/>
<bean id="dog2" class="com.haoyun.POJO.Dog"/><bean id="People" class="com.haoyun.POJO.People" p:name="asdfadf"/>
@Resource(type = Dog.class)
@Resource(name = "cat1")
使用注解开发
在Spring4之后,要使用注解开发,必须要保证AOP的包导入了
<!--指定要扫描的包,这个包小的注解就会生效-->
<context:component-scan base-package="com.haoyun.POJO"/>
@Repository
public class User {@Value("haoyun")private String name ;
}
@Service
@Controller
@Scope("singleton")
使用Java的方式配置Spring
完全不使用XML配置文件,全部使用Java的方式来做
@Configuration
@ComponentScan("com.haoyun.POJO")
public class HaoyunConfig {@Beanpublic User UserBean() {return new User();}}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {@AliasFor(annotation = Component.class)String value() default "";boolean proxyBeanMethods() default true;
}
@Test
public void UserTest() {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(HaoyunConfig.class);//"com.haoyun.controller"/*要与HaoyunConfig中@Bean的method方法名对应*/User user = (User) context.getBean("UserBean");System.out.println(user.getName());}
@Configuration
@ComponentScan("com.haoyun.POJO")
@Import(config2.class)
public class HaoyunConfig {@Beanpublic User UserBean() {return new User();}}
JavaConfig是spring的一个子项目,是spring4之后的核心功能
代理模式SpringAOP的底层,SpringAOP和SpringMVC必问
代理模式:
- 静态代理
- 动态代理
静态代理
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
租房例子
客户找中介,中介找房东,实现租房的方法,还能添加一些附属操作
代码步骤:
- 接口
- 真实角色
- 代理角色
- 客户端访问角色
代理模式的好处:
- 可以使真实角色的操作更加存粹,不用去关注一些公共的业务
- 公共业务交给代理角色,实现业务分工
- 公共业务发生扩展时,方便管理
缺点:
- 一个真实角色就会产生一个代理角色,代码量增多
如果一条线已经做出来了,突然要在中间加一个功能,不能去修改原有的业务代码,在公司中是大忌,只能在原有的基础上添加功能,通过代理,调用的是原有的功能,但是在调用原有的功能的同时能像上面的租房的例子一样,增加一些功能,要去学习下面向对象七大特性
动态代理
静态代理每代理一个被代理角色就要重新编写一个静态代理对象,代码量也十分庞大
动态代理的代理类是动态生成的,不是直接写好的
动态代理分两大类:
- 基于接口的动态代理
- JDK原生的动态代理【这里使用】
- 基于类的动态代理
- 基于类的cglib
- 但是现在用的比较多的是基于java字节码来实现的JavaAssist
需要了解两个类:Proxy,InvocationHandle
- Proxy代理
- InvocationHandle调用处理程序
通过反射的方式,判断被代理对象的类型,处理代理实例
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);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Object result = method.invoke(target,args);return result;}
}
这个代理对象能添加功能,也能执行被代理对象的方法
AOP不改变原有的业务,增加功能
AOP在Spring中的作用
- 横切关注点:跨越应用程序多个模块的方法或功能,于我们业务逻辑无关,但需要关注的部分
- 切面(aspect):横切关注点被模块化的特殊对象,是一个类
- 通知(Adivce):切面必须要完成的工作,类中的一个方法
- 目标(Target):被通知的对象
- 代理(Proxy):向目标对象应用通知后创建的对象
- 切入点(PointCut):切面通知 执行的“地点”的定义
- 连接点(JointPoint):于切入点匹配的执行点
SpringAOP中,通过Advice定义横切逻辑,支持五种类型的的advice
AOP不改变原有代码的情况下,去增加新的功能
使用Spring实现AOP
使用Spring的API接口
<dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version>
</dependency>
xmlns:aop="http://www.springframework.org/schema/aop"
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd
public interface UserService {public void add();public void delete();public void update();public void select();
}
public class UserServiceImpl implements UserService{public void add() {System.out.println("add");}public void delete() {System.out.println("delete");}public void update() {System.out.println("update");}public void select() {System.out.println("select");}
}
public class Log implements MethodBeforeAdvice {///**// * @param method:要执行目标对象的方法// * @param args:参数// * @param target:目标对象// * @throws Throwable// */public void before(Method method, Object[] args, Object target) throws Throwable {System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行");}
}
<bean id="log" class="com.haoyun.log.Log"/>
<bean id="UserServiceImpl" class="com.haoyun.service.UserServiceImpl"/>
<aop:config><!--切入点:expression:表达式(要执行的位置,UserServiceImpl.要切入的方法)--><aop:pointcut id="pointcut" expression="execution(* com.haoyun.service.UserServiceImpl.*(..))"/><!--执行环绕增加--><!--把log切片,切入到pointcut切入点上--><aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
@Test
public void ProxyTest() {ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");/*代理的是一个接口*/UserService userserviceImpl = (UserService) context.getBean("UserServiceImpl",UserService.class);userserviceImpl.add();
}
使用自定义类
public class aspectDiy {public void beforeDiy(){System.out.println("beforeDiy");}public void afterDiy(){System.out.println("afterDiy");}
}
<aop:config><aop:aspect ref="aspectDip"><!--切入点--><aop:pointcut id="pointcut" expression="execution(* com.haoyun.service.UserServiceImpl.*(..))"/><!--通知,切入到哪个点上--><aop:before method="beforeDiy" pointcut-ref="pointcut"/><aop:after method="afterDiy" pointcut-ref="pointcut"/></aop:aspect></aop:config>
beforeDiy
add
afterDiy
使用注解实现
<aop:aspectj-autoproxy/><context:component-scan base-package="com.haoyun.pointCut"/>
@Aspect
@Component
public class AnnotationPointCut {@Before("execution(* com.haoyun.service.UserServiceImpl.*(..))")public void before(){System.out.println(AnnotationPointCut.class.getName()+" before");}@After("execution(* com.haoyun.service.UserServiceImpl.*(..))")public void after(){System.out.println(AnnotationPointCut.class.getName()+" after");}}
规范点的化使用第一种方法吧,切面小的话使用第二种方法,第三种方法也不是很简便,要设置切入点还挺麻烦,第一和第二种方法的切入点都可以复用,第三种方法切入点不好复用,差点意思
整合Mybatis回顾Mybatis
步骤:
<dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.11</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>5.2.7.RELEASE</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.12</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.7.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.7.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.5</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>2.0.5</version></dependency></dependencies><build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources></build>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><package name="com.haoyun.POJO"/></typeAliases><environments default="development"><environment id="development"><transactionManager type="JDBC"/><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments><mappers><package name="com.haoyun.mapper"/></mappers>
</configuration>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private int id;private String name;private String pwd;
}
public interface UserMapper {List<User> selectUser();}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.haoyun.mapper.UserMapper"><select id="selectUser" resultType="user">select * from user</select>
</mapper>
@Test
public void UserMapper(){String resource ="mybatis-config.xml";InputStream inputStream = null ;try {inputStream = Resources.getResourceAsStream(resource);} catch (IOException e) {e.printStackTrace();}SqlSessionFactory build = new SqlSessionFactoryBuilder().build(inputStream);SqlSession sqlSession = build.openSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);List<User> users = mapper.selectUser();for (User user : users) {System.out.println(user);}sqlSession.close();
}
mybatis-spring官方文档
http://mybatis.org/spring/zh/index.html
这里面涉及到mybatis-spring包的版本问题,如果spring版本和mybatis是5.0和3.5版本以上的,要使用Mybatis-spring2.0以上的包
最好就通过阅读mybatis-spring文档来学习
http://mybatis.org/spring/zh/getting-started.html
整合Mybatis方式一
spring的IoC特性,原先的Mybatis中的new对象方法都将制作为bean,进行依赖注入
<!--DateSource:使用spring的数据源替换mybatis的设置:c3p0、dbcp、druid-->
<bean id="dateSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/><property name="username" value="root"/><property name="password" value="123456"/>
</bean>
<!--sqlSessionFactory-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><!--配置数据源--><property name="dataSource" ref="dateSource" /><!--mybatis的核心配置文件,但是可以不写--><property name="configLocation" value="classpath:mybatis-config.xml"/><!--相当于mappe package 添加mybatis.xml配置文件--><property name="mapperLocations" value="classpath:com/haoyun/mapper/UserMapper.xml"/><!--mybatis配置文件中能配置的在这都能配置-->
</bean>
<!--SqlSessionTemplate:就是SqlSession-->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"><!--只能使用构造器注入sqlSessionFactory,因为SqlSessionTemplate没有set方法--><constructor-arg index="0" ref="sqlSessionFactory"/>
</bean>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><typeAliases><package name="com.haoyun.POJO"/></typeAliases>
<!-- <settings>-->
<!-- <setting name="" value=""/>-->
<!-- </settings>--><!--一般会留下几个设置,证明使用了mybatis--><!-- <environments default="development">-->
<!-- <environment id="development">-->
<!-- <transactionManager type="JDBC"/>-->
<!-- <dataSource type="POOLED">-->
<!-- <property name="driver" value="com.mysql.cj.jdbc.Driver"/>-->
<!-- <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=false&useUnicode=true&characterEncoding=UTF-8"/>-->
<!-- <property name="username" value="root"/>-->
<!-- <property name="password" value="123456"/>-->
<!-- </dataSource>-->
<!-- </environment>-->
<!-- </environments>-->
<!-- <mappers>-->
<!-- <package name="com.haoyun.mapper"/>-->
<!-- </mappers>-->
</configuration>
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
public class UserMapperImpl implements UserMapper {private SqlSessionTemplate sqlSession;public void setSqlSession(SqlSessionTemplate sqlSession) {this.sqlSession = sqlSession;}public List<User> selectUser() {UserMapper mapper = sqlSession.getMapper(UserMapper.class);return mapper.selectUser();}
}
<mapper namespace="com.haoyun.mapper.UserMapper"><select id="selectUser" resultType="user">select * from user</select>
</mapper>
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsd"><import resource="spring-dao.xml"/><bean id="userMapper" class="com.haoyun.mapper.UserMapperImpl"><property name="sqlSession" ref="sqlSession"/></bean>
</beans>a
@Test
public void UserMapper(){ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");UserMapper userMapper = context.getBean("userMapper", UserMapper.class);List<User> users = userMapper.selectUser();for (User user : users) {System.out.println(user);}
}
整合Mybatis方式二
<!--SqlSessionTemplate:就是SqlSession-->
<!-- <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">-->
<!-- <!–只能使用构造器注入sqlSessionFactory,因为SqlSessionTemplate没有set方法–>-->
<!-- <constructor-arg index="0" ref="sqlSessionFactory"/>-->
<!-- </bean>-->
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{/*省去了注入的过程*/public List<User> selectUser() {return getSqlSession().getMapper(UserMapper.class).selectUser();}
}
<bean id="UserMapperImpl2" class="com.haoyun.mapper.UserMapperImpl2"><property name="sqlSessionFactory" ref="sqlSessionFactory"/>
</bean>
声明式事务
回顾事务transaction
- 事务在项目开发中十分重要,涉及到数据的一致性
- 事务ACID原则
- 原子性
- 一致性
- 隔离性
- 多个业务可能操作同一个资源,防止数据损坏
- 持久性
- 事务一旦提交,无论系统发生什么问题,结果都不会被影响
这里举了一个例子,进行insert操作和delete操作,insert操作成功,delete操作失败,根据ACID原则,这一组业务应该一起成功或者一起失败
在官方文档中介绍了两种事务的实现类型
http://mybatis.org/spring/zh/transactions.html#configuration
- 交由容器管理事务
- 编程式事务管理
- 根据AOP的特性,最好以横切关注点的方式将切片切入,而不是修改已经写好的业务代码,所以编程式事务管理了解下就行,少用
交由容器管理事务
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><constructor-arg ref="dataSource" />
</bean>
<tx:jta-transaction-manager />
<property name="transactionFactory"><bean class="org.apache.ibatis.transaction.managed.ManagedTransactionFactory" /></property>
但是举例是结合AOP进行的事务管理
<?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"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/aophttps://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd">
<tx:jta-transaction-manager/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><constructor-arg ref="dateSource" />
</bean>
<!--结合AOP实现事务织入-->
<!--配置事务的传播特性,这个属性在晚上搜,事务的传播特性-->
<tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><tx:method name="add" propagation="REQUIRED"/><!--这个属性默认的--><tx:method name="delete" propagation="REQUIRED"/><tx:method name="update" propagation="REQUIRED"/><tx:method name="query" read-only="true"/><!--只读--><tx:method name="*" propagation="REQUIRED"/></tx:attributes>
</tx:advice>
<aop:config><aop:pointcut id="txPointCut" expression="execution(* com.haoyun.mapper.*.*(..))"/><aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
</aop:config>
<select id="selectUser" resultType="user">select * from user
</select>
<insert id="insertUser" parameterType="user">insert into mybatis.user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>
<delete id="deleteUser" parameterType="int">deletes from user where id = #{id}
</delete>
public interface UserMapper {List<User> selectUser();int insertUser( User user);int deleteUser( int id);
}
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{/*省去了注入的过程*/public List<User> selectUser() {User user = new User();user.setId(14);user.setName("haoyun");user.setPwd("123");UserMapper mapper = getSqlSession().getMapper(UserMapper.class);mapper.insertUser(user);mapper.deleteUser(5);return getSqlSession().getMapper(UserMapper.class).selectUser();}public int insertUser(User user) {return getSqlSession().getMapper(UserMapper.class).insertUser(user);}public int deleteUser(int id) {return getSqlSession().getMapper(UserMapper.class).deleteUser(id);}
}