1 Spring
简介:目的:为了解决企业应用开发的复杂性spring是一个轻量级控制反转和aop的容器框架
- 优点:
- spring是一个开源的免费框架(容器)
- spring是一个轻量级非入侵式框架,非入侵就是引入该框架不会对原来代码产生影响
- 支持事务处理,对框架整合支持
- ioc,aop
- 总结一句话就是spring是一个轻量级的控制反转和面向切面编程的aop的框架
1.1spring的组成
1.2 ioc思想
ioc的本质
ioc就是控制反转是一种设计思想,在早期开发过程当用户发来请求,主动权实在我们程序员手指,由程序员手动去new 一个dao接口实现类,这样做耦合程度大大提升,完全是硬编码在程序中。主动权在程序员手中
控制反转后对象的创建转移给第三方个人认为所谓控制反转就是获得对象的方式反转了。使用set方法进行注入,原理 主动权在用户DI只不过是实现ioc的一种方法。
总结就是对象由spring创建,管理装配
2配置bean方法
2.1 通过bean标签;
<bean id="user" class="com.kui.pojo.User">
<property name="name" value="张三"></property>
</bean>
ioc创建对象默认调用无参构造;
当我们设置了有参构造时且未设置无参构造,还按照上面的方法执行肯定会报错,所以需要另一种方法通过构造方法注入值。
根据下标注入
<bean id="user" class="com.kui.pojo.User">
<!-- <property name="name" value="张三"></property>-->
<constructor-arg index="0" value="zhangsi"></constructor-arg>
</bean>
根据成员变量属性类型注入,不过不推荐使用
<bean id="user" class="com.kui.pojo.User">
<!-- <property name="name" value="张三"></property>-->
<!-- <constructor-arg index="0" value="zhangsi"></constructor-arg>-->
<constructor-arg type="java.lang.String" value="zhanga"></constructor-arg>
</bean>
根据成员变量属性名注入
<bean id="user" class="com.kui.pojo.User">
<!-- <property name="name" value="张三"></property>-->
<!-- <constructor-arg index="0" value="zhangsi"></constructor-arg>-->
<!-- <constructor-arg type="java.lang.String" value="zhanga"></constructor-arg>-->
<constructor-arg name="name" value="zhangs"></constructor-arg>
</bean>
当我们配置bean后,spring会自动帮我们创建对象执行构造方法
总结,当spring加载配置文件后bean对象就被创建了
2.1 别名
<bean id="user" class="com.kui.pojo.User">
<!-- <property name="name" value="张三"></property>-->
<!-- <constructor-arg index="0" value="zhangsi"></constructor-arg>-->
<!-- <constructor-arg type="java.lang.String" value="zhanga"></constructor-arg>-->
<constructor-arg name="name" value="zhangs"></constructor-arg>
</bean>
<alias name="user" alias="asdasd"></alias>
且在bean标签中可以起多个别名用,或;分割;
2.1 import标签
导入配置
3 依赖注入(DI)
3.1构造器注入
3.2 set方式注入
本质就是set注入
- 依赖:bean对象的创建依赖于容器
- 容器:bean对象中的所有属性依赖于容器来注入
3.3 第三方注入
拓展方式注入
比如p标签c标签
4 Scope作用域
singleton 默认是单例
prototype多例,原型模式
request,session,application
只能在web中使用
一般单线程使用单例,多线程使用多例,多例消耗资源
5 spring的自动装配方式
自动装配是满足bean依赖的一种方式
spring会在上下文自动寻找并自动给bean配置属性
三种装配方式
- 在xml中配置
- 在java代码中配置
- 隐式的配置(重要)
<bean id="student" class="com.kui.pojo.Student" autowire="byName">
<bean id="address" class="com.kui.pojo.Address">~
通过Byname Spring会自动寻找并配置成员属性address
<bean id="student" class="com.kui.pojo.Student" autowire="byType">
<bean id="address" class="com.kui.pojo.Address">~
通过ByType Spring会自动寻找并配置成员属性address,要求全局配置文件类型只有一个
使用注解装配
首先要添加注解支持
@Autowired
当定义@autowired(requred=false)时说明注入的对象可以为null否则不允许为空。
@Qualifire
可以通过此注解注入特定的bean比如@qualifire(value=“user1”)
如果autowired配置环境比较复杂我们可以通过@qualifire指定id去注入
@Resource
他不是spring中的注解但也可以帮我们注入
resource和autowired的区别
-
都是通过放在属性字段上装配的
-
autowired是通过byType注入,对象必须存在否则空指针异常
如果autowired bytype不唯一则需要通过qualifire指定注入
resource是先通过byname,如果注入失败则通过byType注入 -
执行顺序不同
@Nullable
表示可以为空
6 使用注解开发
- bean
- 属性如何注入
- 衍生的注解
- 自动装配置
- 作用域
bean :使用注解开发必须要导入aop的包,导入context约束增加注解的支持
首先在配置文件中要添加
<context:component-scan base-package="com.kui.pojo"/>
<context:annotation-config></context:annotation-config>
通过value注入 :通过@Value注入值
如果是简单的推荐注解注入,复杂的还是需要配置文件
衍生的注解
@Component
@Controller,@Service,@Repository 他们和@Component一样都可以被容器识别注入。
自动装配置
就是@autowired,@qualifire,@resource,在上面我们说过了
作用域
@Scope可以配置单例或原型。配合以上注解使用
@Scope(“singleton”)
小结 :xml更加万能,适用于任何场合更加容易维护,注解维护更为复杂,
使用场合:
- xml用来管理bean配置属性
- 注入通过注解注入
7 完全使用java的方式来配置Spring
意思就是不使用Xml文件配置。
JavaConfig是Spring的子项目,在Spring4之后,他成了核心功能
用法 创建Config包
- 添加@Configuration注解
- 在类中配置bean,添加@Bean注解
编写Config类
@Configuration它本身也是一个component,由ioc容器管理,他代表的是一个配置类相当于之前的bean。xml。
这其中的getUser就相当于bean标签中的id属性,方法的返回值相当于bean标签中的class属性,添加@bean相当于一个bean标签
@Configuration
@ComponentScan
public class KuiConfig {
@Bean
public User getUser(){
return new User();
}
}
在测试类中测试:这和以往的XMl读取不同,我们要使用全Java配置所以需要得到配置对象
// ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beans1.xml");
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(KuiConfig.class);
User user =(User) applicationContext.getBean("getUser");
System.out.println(user);
这回是通过加载配置类得到bean;
通过 @import(class配置文件)导入其他配置类
8代理模式
8.1静态代理
角色分析
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:一般被代理的角色
- 代理角色:代理真实的角色,代理真实角色后一般会进行一些附属操作
- 客户:访问代理对象得人
代码简单实现以下:
租房子得人
package com.kui.pojo;
public class Client {
public static void main(String[] args) {
// Host host=new Host();
Proxy proxy=new Proxy(new Host());
proxy.rent();
}
}
package com.kui.pojo;
public class Host implements Rent {
public void rent(){
System.out.println("出租房");
}
}
package com.kui.pojo;
public class Proxy implements Rent{
private Host host;
public Proxy() {
}
public Proxy(Host host) {
this.host = host;
}
public void rent() {
seeHouse();
host.rent();
hetong();
}
public void seeHouse(){
System.out.println("看房子");
}
public void hetong(){
System.out.println("签合同");
}
}
package com.kui.pojo;
public interface Rent {
void rent();
}
运行结果:
代理模式的好处
- 可以使真实角色的操作更加纯粹不用去关心公共业务
- 公共也就交给了代理角色,实现了业务的分工
- 公共业务扩展的时候方便集中管理
缺点
- 一个真实角色就会产生一个代理角色,代码量翻倍
9 动态代理
- 动态代理和静态代理中拥有的角色是相同的
- 动态代理的类是动态生成的,不是写好的
- 动态代理分为两大类
1.基于接口
jdk动态代理(我们使用这种)
2基于类的:cglib
3基于字节码实现:javasist
需要了解两个类:Proxy,InvocationHndler:调用处理程序
public class Client {
public static void main(String[] args) {
Host host=new Host();
MyInvocationHandler myInvocationHandler=new MyInvocationHandler();
myInvocationHandler.setObject(host);
Rent proxy = (Rent) Proxy.newProxyInstance(myInvocationHandler.getClass().getClassLoader(), host.getClass().getInterfaces(),myInvocationHandler);
proxy.add();
}
}
public class Host implements Rent {
public void rent(){
System.out.println("出租房");
}
public void add() {
}
public void query() {
}
}
public interface Rent {
void rent();
void add();
void query();
}
public class MyInvocationHandler implements InvocationHandler {
private Object object;
public void setObject(Object object) {
this.object = object;
}
public Object getProxy(){
return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log(method.getName());
Object result= method.invoke(object,args);
return result;
}
public void log(String name){
System.out.println("执行了"+name+"fangfa");
}
}
实现原理及过程:
我们需要自定义一个invocationHandler实现类,在invoke中实现需要增强的方法,并该对象方法中创建Proxy到代理对象;
9.1Aop
他是spring中重要的特性,主要是面向切面编程,正常的业务是纵向的,aop专注横向操作
实现方法一:使用SpringApi接口
通过配置xml
<bean id="UserServiceImpl" class="com.kui.service.UserServiceImpl"></bean>
<bean id="log" class="com.kui.Log"></bean>
<bean id="afterLog" class="com.kui.AfterLog"></bean>
<aop:config >
<aop:pointcut id="pointcut" expression="execution(* com.kui.service.UserServiceImpl.*(..))"/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"></aop:advisor>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"></aop:advisor>
</aop:config>
添加日志记录对象
public class AfterLog implements AfterReturningAdvice {
public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
System.out.println(o1.getClass().getName()+"de"+method.getName()+"被执行了");
}
}
实现方法二:通过自定义来实现Aop
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.kui.service.UserServiceImpl.*(..))"/>
<aop:aspect ref="div">
<aop:before method="before" pointcut-ref="pointcut"></aop:before>
<aop:after method="after" pointcut-ref="pointcut"></aop:after>
</aop:aspect>
</aop:config>
public class Log {
void before(){
System.out.println("before");
}
void after(){
System.out.println("after");
}
}
使用注解实现;
@Aspect
public class Log2 {
@Before("execution(* com.kui.service.UserServiceImpl.*(..))")
void before(){
System.out.println("before");
}
@After("execution(* com.kui.service.UserServiceImpl.*(..))")
void after(){
System.out.println("after");
}
@AfterReturning("execution(* com.kui.service.UserServiceImpl.*(..))")
void afterRunning(){
System.out.println("afterRunning");
}
@Around("execution(* com.kui.service.UserServiceImpl.*(..))")
void around(ProceedingJoinPoint jp) throws Throwable {
Object proceed = jp.proceed();
System.out.println("around");
}
}
在xml中添加
注解扫码
<aop:aspectj-autoproxy/>
值得注意的是在环绕中@Arround
可以传递一个参数
ProceedingJoinPoint
当传入这个参数后需要调用ProceedingJoinPoint.proceed()方法去执行被代理用户的方法,否则不会执行。
10整合Mybatis
第一种方法:
首先配置连接池,数据源和SqlSessionFactorybean
<?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"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf8&useSSL=false&serverTimezone=UTC"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis-config.xml"/>
<property name="mapperLocations" value="classpath:com/kui/dao/*.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
<constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
</bean>
</beans>
配置mybatis别名
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kui.pojo"/>
</typeAliases>
</configuration>
配置mapper bean
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.kui.pojo"/>
</typeAliases>
</configuration>
其中值得注意的是sqlSessionTemlate,他其实就相当于sqlSession,他是线程安全的。可以被多个dao容器共用
第二种实现方法:
通过官方文档的值可以通过Mapper实现类去继承SqlSessionDaosupport
当继承这个抽象类后,我们可以不用配置sqlSessionTemplate bean了,
直接使用getSqlSession()就可以获取sqlSession对象了。
public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper{
public User getUser(String id) {
return getSqlSession().getMapper(UserMapper.class).getUser(1+"");
}
}
11.声明式事务
回顾事务:
- 要么都成功要么都失败,要保证原子性一致性隔离性持久性
- 事务在项目开发过程中涉及到数据一致性的问题不能马虎
事务ACID原则
- 原子性:确保要么都成功要么都失败
- 一致性 :事务的执行不能破坏数据库数据的完整性和一致性,一个事务在执行之前和执行之后,数据库都必须处于一致性状态。
- 隔离性 事务的隔离性是指在并发环境中,并发的事务时相互隔离的,一个事务的执行不能不被其他事务干扰。
- 持久性一旦事务提交,那么它对数据库中的对应数据的状态的变更就会永久保存到数据库中。
AOP中有两种事务管理
- 编程式事务
- 声明式事务
编程式:
就是在代码中添加
声明式事务:即交由容器管理事务,使用AOP去做
流程就是配置transactionManager bean
配置aop切入点
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<constructor-arg name="dataSource" ref="dataSource"></constructor-arg>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add" propagation="REQUIRED">
<tx:method name="selectAllUser" read-only="true">
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="txPointcut" expression="execution(* com.kui.dao.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"></aop:advisor>
</aop:config>
REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY:支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED:支持当前事务,如果当前事务存在,则执行一个嵌套事务,如果当前没有事务,就新建一个事务。