spring细节回顾
1、IOC/DI
ApplicationContext接口是BeanFactory接口的子接口,Spring所有的东西都扔到了这里边。
1、Beans:Spring负责创建类对象并管理对象;
2、Core:核心类;
3、Context:上下文参数,获取外部资源,或者是管理注解等。
4、SPEL:expression包;
IOC功能:
IoC 完成的事情原先由程序员主动通过 new 实例化对象转交给 Spring 负责。
环境搭建:
四大核心jar包要有。
记住spring容器是ApplicationContext。
applicationContext.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">
<!-- id 表示获取到对象标识 class 创建哪个类的对象-->
<bean id="peo" class="com.pshdhx.pojo.People"/>
</beans>
</xml>
Spring配置文件是基于Schema的。
schema文件的扩展名.xsd;
每次引入一个xsd文件都是一个namespace(====xmlns=“”)
如此,引入了xmlns,配置好了xsi的路径,在xml中就可以写引入的配置标签了。
Spring创建对象实例
//正常创建对象
People people = new People();
//利用spring容器创建对象
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
People peo = ac.getBean("peo",People.class);
//注意此处,并不是运行People peo = ac.getBean("peo",People.class);不是才创建的对象,而是一加载(配置文件)上下文的时候,bean对象就被创建了,通过People类的无参构造可以看出来。
Spring整合mybatis
1、必须有核心jar包,spring事务和common包+mybatis+jdbc等。
2、配置web.xml
2.1:需要配置(参数):作用是找到Spring的配置文件。
2.2:需要配置监听类::作用是帮助加载Spring配置文件。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- 上下文参数 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<!-- spring 配置文件 -->
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 封装了一个监听器,帮助加载 Spring 的配置文件 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
</xml>
3、编写applicationContext.xml
3.1:配置数据源的Bean
3.2:配置Mybatis的SqlSessionFactory的Bean,内容属性是指向数据源Bean。
3.3:配置Mybatis的Mapper包扫描器,属性是关联SqlSessionFactoryBean和包扫描basePackage。
3.4:将Service注入到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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 数据源封装类 .数据源:获取数据库连接,spring-jdbc.jar 中-->
<bean id="dataSouce" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssm"></property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
</bean>
<!-- 创建 SqlSessionFactory 对象 -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 数据库连接信息来源于 dataSource -->
<property name="dataSource" ref="dataSouce"></property>
</bean>
<!-- 扫描器相当于 mybatis.xml 中 mappers 下 package 标签,扫描 com.pshdhx.mapper 包后会给对应接口创建对象-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 要扫描哪个包 -->
<property name="basePackage" value="com.pshdhx.mapper"></property>
<!-- 和 factory 产生关系 -->
<property name="sqlSessionFactory" ref="factory"></property>
</bean>
<!-- 由 spring 管理 service 实现类 -->
<bean id="airportService" class="com.pshdhx.service.impl.AirportServiceImpl">
<property name="airportMapper" ref="airportMapper"></property>
</bean>
</beans>
</xml>
Spring无法管理Servlet,因为其提供的接口要想被我们访问,需要被Tomcat的web容器管理。
4、让tomcat启动时自动加载Spring文件【web.xml,被tomcat管理了。】
5、控制器中初始化并且注入ServiceImpl
@WebServlet("/airport")
public class AirportServlet extends HttpServlet{
private AirportService airportService;
@Override
public void init() throws ServletException {
//对 service 实例化
// ApplicationContext ac = newClassPathXmlApplicationContext("applicationContext.xml");
//spring 和 web 整合后所有信息都存放在
webApplicationContext ApplicationContext ac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
airportService=ac.getBean("airportService",AirportServiceImpl.class);
}
@Override
protected void service(HttpServletRequest req,HttpServletResponse resp) throws ServletException,IOException {
req.setAttribute("list", airportService.show());
req.getRequestDispatcher("index.jsp").forward(req,resp);
}
}
2、AOP
1、方法之间是纵向执行的,在某个方法的的前面或者是后面增加一个前置通知或者是后置通知,环绕通知,异常通知等。
2、不需要修改原有的方法,体现出程序的高扩展性。
概念:
1、切点 pointCut
2、前置通知 beforeAdvice
3、后置通知 afterAdvice
4、如果切点异常,会出现异常通知 throwsAdvice
2.1 AOP的demo
1、引入jar包
2、在spring配置文件中引入和配置AOP功能
3、实现通知事件,注意实现接口。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-usjg5bOI-1673189297416)(AOP包的引入.png)]
<?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/aop
http://www.springframework.org/schema/aop/spring-aop.
xsd">
<!-- 配置通知类对象,在切面中引入 -->
<bean id="mybefore" class="com.pshdhx.advice.MyBeforeAdvice"></bean>
<bean id="myafter" class="com.pshdhx.advice.MyAfterAdvice"></bean>
<!-- 配置切面 -->
<aop:config>
<!-- 配置切点 -->
<aop:pointcut expression="execution(* com.pshdhx.test.Demo.demo2())" id="mypoint"/>
<!-- 通知 -->
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
</aop:config>
<!-- 配置 Demo 类,测试使用 -->
<bean id="demo" class="com.pshdhx.test.Demo"></bean>
</beans>
注意后置通知的参数:
arg0:切点方法的返回值;
arg1:切点方法对象;
arg2:切点方法的参数数组;
arg3:切点方法对象。
public class MyAfterAdvice implements AfterReturningAdvice {
@Override
public void afterReturning(Object arg0, Method arg1,Object[] arg2, Object arg3) throws Throwable {
System.out.println("执行后置通知");
}
}
2.2 AOP的要点**
2.2.1 配置表达式
‘*’ 是通配符,匹配任意方法名,任意类名,任意一级包名
注意参数:* com.pshdhx.test.Demo.demo2((String,int) and args(name,age))
com.pshdhx.test.Demo.demo2(…)是匹配任意类型参数,但是不能直接使用。
<aop:pointcut expression="execution( *com.pshdhx.test.Demo.demo1(String,int)) and args(name1,age1)" id="mypoint"/>
<aop:pointcut expression="execution(*
com.pshdhx.test.Demo.demo1(String)) and args(name1)"
id="mypoint1"/>
注意execution的括号,不是全部的,而是方法的。参数的有另外的括号。
2.2.2 参数的配置
<aop:before method="mybefore"
pointcut-ref="mypoint" arg-names="name1,age1"/>
切点配置的参数可以不用和java方法的参数完全相同。
但是在通知配置上,参数的个数和名称必须和切点的配置相同,而且在java中的参数名称必须和通知配置上完全相同。
2.2.3 执行顺序
切点通知的执行顺序和切点配置的顺序有关。
2.3 实现AOP的两种方式
2.3.1 schema Base
前置通知和后置通知要实现固定的接口,在接口的方法中定义通知。
2.3.2 aspectJ方式
定义普通的类和普通的通知方法,但是要在配置文件中进行类和方法的配置。
以ThrowAdvice为例:当切点处有异常时,进行通知。并且通知在Service层不能够tryCatch,否则在最前边就捕获不到了。执行Demo.demo1时,无论是Throws或者是,TryCatch都能触发异常通知。
public class ThrowAdvice{
public void printAdvice(Exception e){
System.out.println("执行了异常通知"+e.getMessage());
}
}
<bean id="throwAdvice" class="com.pshdhx.advice.ThrowAdvice"></bean>
<aop:config>
<aop:aspect ref="throwAdvice">
<aop:pointcut expression="execution(* com.pshdhx.test.Demo.demo(String,int)) and args(name1,age1)" id="mypoint"/>
<aop:pointcut expression="execution(* com.pshdhx.test.Demo.demo1(String)) and args(name1)" id="mypoint1"/>
<aop:before method="mybefore" pointcut-ref="mypoint" arg-names="name1,age1"/>
<aop:before method="mybefore1" pointcut-ref="mypoint1" arg-names="name1"/>
<aop:after method="myafter" pointcut-ref="mypoint"/>
<aop:after-returning method="myaftering" pointcut-ref="mypoint"/>
<aop:after-throwing method="throwAdvice" pointcut-ref="mypoint" throwing="e"/>
<aop:around method="myarround" pointcut-ref="mypoint"/>
</aop:aspect>
</aop:config>
2.3.3 环绕通知
前置通知+后置通知=环绕通知。
public class MyArround implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation arg0) throws Throwable {
System.out.println("环绕-前置");
Object result = arg0.proceed();//放行,调用切点方式,切点返回值
System.out.println("环绕-后置");
return result;
}
}
<bean id="myarround"class="com.pshdhx.advice.MyArround"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.pshdhx.test.Demo.demo1())" id="mypoint"/>
<aop:advisor advice-ref="myarround" pointcut-ref="mypoint" />
</aop:config>
<bean id="demo" class="com.pshdhx.test.Demo"></bean>
2.4 基于注解 Aspect方式***
Spring是不会自动寻找注解的,必须告诉Spring哪些包下有注解。
新增引入:
<beans xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
">
</beans>
扫描的包如果是多个,用,隔开。
<context:component-scan base-package="com.pshdhx.t1,com.psd.t2"></context:component-scan>
2.4.1 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"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.pshdhx.aop"></context:component-scan>
<!--
1、设置为true,是使用cglib动态代理
2、设置为false,是使用jdk动态代理
Are class-based (CGLIB) proxies to be created?
By default, standard Java interface-based proxies are created.
是否要创建基于类的(CGLIB)代理?默认情况下,标准创建了基于Java接口的代理。
-->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
</beans>
2.4.2 切点代码
package com.pshdhx.aop;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Component
public class PointCutClass {
@Pointcut("execution(* com.pshdhx.aop.PointCutClass.testAop())")
public void testAop(){
System.out.println("测试AOP--这是切点");
}
}
2.4.3 通知代码
package com.pshdhx.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class Advice {
@Before("com.pshdhx.aop.PointCutClass.testAop()")
public void myBefore(){
System.out.println("前置通知!!!!");
}
@After("com.pshdhx.aop.PointCutClass.testAop()")
public void myafter(){
System.out.println("后置通知");
}
@AfterThrowing("com.pshdhx.aop.PointCutClass.testAop()")
public void mythrow(){
System.out.println("异常通知");
}
@Around("com.pshdhx.aop.PointCutClass.testAop()")
public Object myarround(ProceedingJoinPoint p) throws
Throwable{
System.out.println("环绕-前置");
Object result = p.proceed();
System.out.println("环绕-后置");
return result;
}
}
2.4.4 测试代码
package com.pshdhx;
import com.pshdhx.aop.PointCutClass;
import com.pshdhx.utils.SpringUtils;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Author: pansd
* Date: 2022/12/4 16:44
* Description: 测试主类
*/
public class Test {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
PointCutClass pointCutClass = ac.getBean("pointCutClass", PointCutClass.class);
pointCutClass.testAop();
}
}
3、代理设计模式
“设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。 使用设计模式是为了可重用代码、让代码更容易被他人理解、提高代码的可靠性。
AOP就是使用了代理设计模式;
3.1 什么是代理设计模式
为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处为可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
3.1.1 代理设计模式的优点
1、保护真实对象;(总裁)
2、让真实对象的职责更加明确和集中;(秘书做杂货)
3、便于扩展。(秘书处)
3.1.2 静态代理设计模式
由代理对象代理所有真实对象的功能,需要自己编写代理类,代理方法的每个功能点都需要重写。
缺点:当代理功能比较多时,代理类中方法需要写很多。
方法:真实对象和代理对象都需要实现同一个接口中的所有的方法。
3.2 JDK的动态代理
优点:jdk自带,不需要额外导入jar包;
缺点:真实对象必须实现接口,利用JDK的反射机制,效率不高;
3.2.1 JDK动态代理的代码
//定义接口
public interface FunctionInterface {
void work();
}
//真实对象Manager实现接口
public class Manager implements FunctionInterface{
@Override
public void work() {
System.out.println("work..............");
}
}
//代理对象实现反射接口,增添功能[InvocationHandler]
public class ProxyObj implements InvocationHandler {
private Manager manager = new Manager();
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("预约时间");
Object result = method.invoke(manager, args);
System.out.println("记录访客信息");
return result;
}
}
//测试方法
public class JDKProxyTest {
public static void main(String[] args) {
ProxyObj p = new ProxyObj();
//第一个参数:反射时使用的类加载器
//第二个参数:Proxy需要实现什么接口
//第三个参数:通过接口对象调用方法时,需要调用哪个类的invoke方法
FunctionInterface instance = (FunctionInterface) Proxy.newProxyInstance(JDKProxyTest.class.getClassLoader(),
new Class[]{FunctionInterface.class}, p);
instance.work();
}
}
3.3 Cglib代理
实现原理:子类继承父类,重写父类的方法。super.work()前后添加功能;
cglib的优点:
1、它是基于字节码,不是基于反射,生成真实对象的代理对象,所以效率比jdk动态代理效率高;
缺点:
1、需要额外引入第三方的jar包。
asm包:解析字节码的包;cglib:功能jar包;
spring使用AOP功能时,真实对象和代理对象转换时,注意:
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
设置为true时,使用cglib动态代理;
设置为false,使用jdk动态代理。
3.3.1 cglib动态代理代码
public class Manager {
public void work(){
System.out.println("工作中....");
}
}
//注意代理类实现cglib的接口
public class CglibProxy implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用方法前");
// method.invoke(objects);
methodProxy.invokeSuper(o,objects);
System.out.println("调用方法后");
return null;
}
}
//cglib的测试类
package com.pshdhx;
import com.pshdhx.cglib.CglibProxy;
import com.pshdhx.cglib.Manager;
import net.sf.cglib.proxy.Enhancer;
/**
* Author: pansd
* Date: 2022/12/7 22:01
* Description: TODO
*/
public class CglibTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Manager.class);
enhancer.setCallback(new CglibProxy());
Manager manager = (com.pshdhx.cglib.Manager) enhancer.create();
manager.work();
}
}
4、SSM的配置文件整合
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
applicationContext.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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="url" value="jdbc:mysql://localhost:3306/ssm"></property>
<property name="username" value="root"></property>
<property name="password" value="smallming"></property>
</bean>
<!-- SqlSessinFactory对象 -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="typeAliasesPackage" value="com.pshdhx.pojo"></property>
</bean>
<!-- 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.pshdhx.mapper"></property>
<property name="sqlSessionFactory" ref="factory"></property>
</bean>
<!-- 注入 -->
<bean id="usersService" class="com.pshdhx.service.impl.UsersServiceImpl">
<property name="usersMapper" ref="usersMapper"></property>
</bean>
<!-- aop -->
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy>
<bean id="mybefore" class="com.pshdhx.advice.MyBefore"></bean>
<bean id="myafter" class="com.pshdhx.advice.MyAfter"></bean>
<aop:config>
<aop:pointcut expression="execution(* com.pshdhx.service.impl.UsersServiceImpl.login(..))" id="mypoint"/>
<aop:advisor advice-ref="mybefore" pointcut-ref="mypoint"/>
<aop:advisor advice-ref="myafter" pointcut-ref="mypoint"/>
</aop:config>
</beans>
4.1 原始servlet控制器
package com.pshdhx.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.pshdhx.pojo.Users;
import com.pshdhx.service.UsersService;
import com.pshdhx.service.impl.UsersServiceImpl;
//一启动就直接进入到login目录下,寻找index.jsp,我们需要到login.jsp
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
private UsersService usersService;
@Override
public void init() throws ServletException {
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
usersService = wac.getBean("usersService",UsersServiceImpl.class);
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
Users users = new Users();
users.setUsername(req.getParameter("username"));
users.setPassword(req.getParameter("password"));
Users user = usersService.login(users);
if(user!=null){
resp.sendRedirect("main.jsp");
}else{
resp.sendRedirect("login.jsp");
}
}
}
4.2 AOP和Filter的区别
AOP拦截的是切点的方法;
Filter拦截的是某个请求;
4.3 属性的自动注入
<!-- 原始方式 -->
<bean id="teacher" class="com.pshdhx.test.Teacher"></bean>
<bean id="people" class="com.pshdhx.test.People">
<property name="teacher" ref="teacher"></property>
</bean>
<bean id="people" class="com.pshdhx.test.People" autowire="default"></bean>
<!-- 实际上有五个值,default(默认走上边的全局,全局也默认是default),
no (不自动注入),
byName(找其他bean的id)也向容器中去寻找@Component("pshdhx"),
byType(找全局类型,往class中对应,但是绝对不能出现两个及其以上的注入),
contructor(必须要在people类中提供一个构造方法,有Teacher参数的和无参构造,但是找类的时候,也是通过byName找的,如果上边的id变为了teacher1,则找寻不到。teacher=null) -->
全局注入:直接在xsi:schemaLocation=“写就行”
default-autowire=“byName”
总结:基本上就是省略了ref指向的那一行属性的配置。
4.4 spring加载外部属性文件
首先xsi引入context
自动注入的优先级特别高,会先进行实例的注入。但是实例的值,是用的外部文件引入的,此时外部文件的值还没有被加载进spring,所以会报错。
自动注入影响的是只有ref的指向对象,而不影响字符串(sqlSessionFactoryBeanName)。
<!-- SqlSessinFactory对象 -->
<bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- <property name="dataSource" ref="dataSource"></property> -->
<property name="typeAliasesPackage" value="com.pshdhx.pojo"></property>
</bean>
<!-- 扫描器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.pshdhx.mapper"></property>
<!-- <property name="sqlSessionFactory" ref="factory"></property> -->
<property name="sqlSessionFactoryBeanName" value="factory"></property>
</bean>
db.properties
jdbc.driverName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=pshdhx
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:db.properties,classpath:second.properties" />
<!-- 数据源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driverName}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
4.5 scope属性
//<bean id="teacher" class="com.pshdhx.vo.Teacher" scope="singleton"></bean> <!-- 默认单例 -->
Teacher t1 = ac.getBean("teacher", Teacher.class);
Teacher t2 = ac.getBean("teacher", Teacher.class);//teacher区分大小写
System.out.println(t1==t2);//true
单例模式:一个bean对应的对象,默认是单例的,在整个对象中只初始化一次。无论获取多少次对象,只初始化一次。
<bean id="teacher" class="com.pshdhx.vo.Teacher" scope="singleton"></bean> <!-- 默认单例 -->
<bean id="teacher" class="com.pshdhx.vo.Teacher" scope="prototype"></bean> <!-- 改为多例 -->
request:每次请求,重新实例化;所以是web对象;
作用:在一个应用程序中,保证最多有一个实例。
优点:
1、提升运行效率;
2、实现数据共享;案例:application对象(单例)是四大作用于对象,有效范围是tomcat启动到关闭一直有效。
4.6 手写单例
public class SingleTon{
//由于对象需要静态方法调用,把获取对象的方法设置为static,方法设置为了static,则变量也设置为static
//由于对象是static,必须设置访问权限修饰符为private,如果是public,则可以通过类直接调用对象了,不走获取对象的接口
private static SingleTon singleTon;
private SingleTon(){}
private static SingleTon getInstance(){
if(singleTon == null){
syncronized(SingleTon.class){
//防止多线程情况下,需要再判断一次[双重认证]
if(singleTon == null){
singleTon = new SingleTon();
}
}
}
return singleTon;
}
}
懒汉式,由于添加了锁,效率会低。
饿汉式,解决了懒汉式中多线程访问可能出现同一个对象和效率低下的问题。
public class SingleTon{
private static SingleTon singleTon = new SingleTon();
private SingleTon(){}
public static SingleTon getInstance(){
return singleTon;
}
}
5、声明式事务基本配置步骤
1、编程式事务:提交和回滚自己编写;
2、声明式事务:事务控制代码由Spring写好,程序员只需要声明出那些方法需要进行事务控制和如何进行事务控制。
代码:
3、事务传播行为
name="*"支持通配符
readonly=“boolean" 是否是只读事务
如果为true,则告诉数据库此事务为只读事务。数据查询会优化,对性能有一定提升。所以只是需要查询的方法,建议使用此数据。
如果为false,默认值,增加,删除,修改,建议使用此方法;
propagation=“” 控制事务传播行为,当一个具有事务控制的方法被另一个有控制方法调用后,需要如何管理事务。(新建事务?还是再事务中执行,还是挂起报异常)
required:必须得有事务。默认值。
supports:如果当前有事务,就按照事务来;如果当前没有事务,就按照非事务状态来;
mandatory:必须再事务中执行,如果当前有事务,就在事务中执行,如果没有事务,就报错。
required_new:必须再事务中执行,如果当前有事务,挂起;如果当前没有事务,新开一个事务,两个事务没有任何关系。
not_supported:必须在非事务中执行,如果当前有事务,挂起当前事务。
never:必须在非事务状态下执行,怎么都不能有事务。如果当前有事务,报错。
nested:必须在事务状态下执行,如果没有事务,就新建事务。如果有事务,就创建一个嵌套事务。两个事务有关系,子事务被大事务所包含。
6、 事务隔离级别
在多线程或者是并发访问情况下,如何保证所访问到的数据是对的。
6.1 脏读
事务A读取到事务B中未提交的数据,但是事务B的数据可能发生改变,此时认为事务A读取过程为脏读。
6.2 幻读
事务A按照特定条件查询出结果,事务B去新增了一条符合条件的数据,事务A中查出的数据和数据库中的数据不一致,事务A好像出现了幻觉,此时加表锁。
特点:
针对于的操作是新增和删除数据;
两次事务的结果。
6.3 不可重复读
当事务A在第一次读取数据后,事务B对事务A读取的数据进行了修改,事务A中再次读取的数据和之前读取的数据不一致,该过程为不可重复度。
特点:
主要是针对于某行数据,或者是行中的某一列。
主要是针对的是修改操作。
两次读取在同一个事务内。
场景:张三去取钱,发现余额为20000,想要取出15000。与此同时,他老婆取出了15000,张三再取钱时,发现钱不够了。所以要在读取到20000余额时,对该数据加上行锁。
6.4 序列化(可串行读)
排队操作,对整个表进行加锁。一个事务在操作完成数据之后,另一个事务才能操作这个表。【是最安全的,但是效率最低。】
isolation="READ_COMMITTED" READ_UNCOMMITTE REPEATABLE_READ SERIALIZABLE DEFAULT
6.5 解决和缺陷
读未提交:可以读取未提交的数据,可能出现脏读,幻读,不可重复读。
效率最高;
读已提交:只能读取其他事务中已提交的数据。可以防止脏读。可能出现不可重复度和幻读,【已提交和加锁没有关系】。
REPEATABLE_READ:(Repeatable Read 可重复的读取),可以对读取的数据进行加锁,避免其他人对其进行修改。可以防止不可重复度和脏读(因为加了锁,别人无法对其进行修改),但是不能防止幻读。
SERIALIZABLE
排队操作,对整个表进行加锁。一个事务在操作完成数据之后,另一个事务才能操作这个表。【是最安全的,但是效率最低。】
6.6 代码配置
<!-- spring-jdbc中 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
</bean>
<!-- 配置声明式事务,要想其生效,就必须有切点(基于通知)。真正控制那些方法实现事务,则必须控制具体的方法 -->
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="userInsert" propagation="REQUIRED" isolation="SERIALIZABLE" rollback-for="java.lang.Exception"/>
<tx:method name="ins*"/>
<tx:method name="del*"/>
<tx:method name="upd*"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="mypoint" expression="execution(* com.pshdhx.service.impl.*.*(..))"/>
<!-- 声明式事务相当于通知,多饶了一个圈 -->
<aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint" />
</aop:config>
6.7 异常回滚
rollback-for=“java.lang.Exception” 异常类型全限定路径
当出现什么异常时,需要进行回滚
建议:给定该属性值。
手动抛异常 throw new Exception时,一定要给该属性值。
no-rollback-for=“”
当出现什么异常时,不回滚。
7、常用注解
spring中常用的注解:
1、@Component 创建类对象,相当于
2、@Service与@Component功能相同。
3、@Repository与@Component功能相同,写在数据访问层上。
4、@Controller与@Component功能相同,写在控制器上。
5、@Autowired(spring)不需要写setget方法。默认按照ByType注入。
6、@Resource(javax) 默认按照ByName注入,没有按照ByType注入,不需要写setget方法。
7、@Value获取属性文件内容
8、@PointCut 定义切点
9、@Aspect 定义切面类 @Before @After @AfterReturning(切点必须正常执行)@Around