Spring(IOC、AOP和事务)

目录

Spring介绍

Spring IOC

传统代码对象管理的弊端

实现过程

bean标签属性介绍

对象创建方式

工厂bean

bean的作用域

SpringBean的生命周期***

依赖注入

注解实现IOC

完全注解开发(JavaConfig)

Spring AOP

AOP的实现机制

JDK动态代理实现

CGlib代理

Spring中使用AOP

切入点方法的定义

获取切入点信息

使用AspectJ注解开发

Spring-JDBC 操作数据库

事务


Spring介绍

Spring是一个设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是一个分层的JavaSE/EE full-stack(一站式轻量级开源框架。

 

特点

1.方便解耦,简化开发

通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

2.AOP编程的支持

通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统面向对象实现的功能可以通过AOP轻松应付。

3.声明式事务的支持

在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高开发效率和质量。

4.方便程序的测试

可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。

5.方便集成各种优秀框架

Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如Struts,Hibernate、Hessian、Quartz)等的直接支持。

6.降低Java EE API的使用难度

 

组织结构

ORM- object relation mapping

OXM-Object xml mapping

JMS - Java消息服务(Java Message Service ,JMS)

WebSocket protocol 是HTML5一种新的协议。它实现了浏览器与服务器全双工通信(full-duplex)。一开始的握手需要借助HTTP请求完成。Socket是传输控制层协议,WebSocket是应用层协议。

Portlet是一种Web组件-就像servlets-是专为将合成页面里的内容聚集在一起而设计的。通常请求一个portal页面会引发多个portlets被调用。每个portlet都会生成标记段,并与别的portlets生成的标记段组合在一起嵌入到portal页面的标记内

spring全家桶:spring,Spring Data、Spring MVC、Spring Boot、Spring Cloud(微服务)

 

Spring核心模块

- spring-core:依赖注入IOC与DI的最基本实现

- spring-beans:Bean工厂与bean的装配

- spring-context:spring的context上下文即IoC容器

- spring-context-support

- spring-expression:spring表达式语言

 

Spring IOC

 

IOC是 Inverse of Control 的简写,意思是控制反转。是降低对象之间的耦合关系的设计思想。就是将对象统一管理,统一分配

 

传统代码对象管理的弊端

传统的调用方式时service层调用dao层,但是当我们的需求发生改变的时候,我们需要可能需要在写一个dao的实现类,然后再改变service的继承关系。但是这种改变源代码的方式不利于测试。我们可以用一种方式,就是把所有的对象提取出来,让一个容器来统一的管理

 

实现过程

1、添加jar包

<!-- Spring的核心工具包-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<!--在基础IOC功能上提供扩展服务,还提供许多企业级服务的支持,有邮件服务、 任务调度、远程访问、缓存以及多种视图层框架的支持-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring IOC的基础实现,包含访问配置文件、创建和管理bean等 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring context的扩展支持,用于MVC方面 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<!-- Spring表达式语言 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-expression</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>

2、创建配置文件 applicationContext.xml (放在resource下面)

<?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">
</beans>

3、在配置文件中创建对象

<bean id="对象名" class="类的完整路径">
    <property name="属性名" ref="对象的id值"></property>
</bean>

4、加载配置文件,获得对象

ApplicationContext app=new ClassPathXmlApplicationContext("spring.xml"); 
Users users=(Users)app.getBean("u1");

举例:

模拟一个传统Service调用dao的过程

这里我们设计两个对象的调用过程, 我们可以把这两个对象写到配置文件中

首先new 两个对象

因为在UserServiceImpl中有个UserDao的属性

最后再在从配置文件中得到已经创建好的对象

注意:给属性赋值的时候,系统可以调用set方法,但是通过测试类得到属性的时候,只能调用配置文件

 

bean标签属性介绍

单列:每次拿到的对象都是同一个

对象创建方式

1、无参构造

2、有参构造

3、静态方法创建对象

4、非静态方法

 

工厂bean

spring有两种类型的bean,一种是普通的bean(和上面一样),另一种是工厂bean(FactoryBean)

普通bean:在配置文件中定义bean类型就是返回类型

工厂bean:配置文件中定义的类型和返回的类型不一样

实现步骤:

(1)创建类,让类实现FactoryBean接口

(2)实现接口方法,在实现的方法中定义bean的类型

 

bean的作用域

设置bean的实例是单例还是多例

单例(默认)
<bean id="myBean" class="com.vv.bean.MyBean" scope="singleton"></bean>
多例
<bean id="myBean" class="com.vv.bean.MyBean" scope="prototype"></bean>

设置指为singleton时:加载配置文件的时候就会创建对象

设置值为prototype时:在调用getBean方法的时候才创建多实例对象

 

SpringBean的生命周期***

1)根据配置文件调用 Bean 构造方法或工厂方法实例化 Bean。

2)利用依赖注入(DI)完成 Bean 中属性值的配置注入。

3)如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前Bean 的 id 值。

4)如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。

5)如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。

6)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方postProcessBeforeInitialzation() 对Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。

7)如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。初始化bean的时候执行,可以针对某个具体的bean进行配置。afterPropertiesSet 必须实现 InitializingBean接口。实现 InitializingBean接口必须实现afterPropertiesSet方法。

8)如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。

9)如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。

10)如果在中指定了该 Bean 的作用范围为 scope="singleton",则将该 Bean 放入 Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在中指定了该 Bean 的作用范围为scope="prototype",则将该 Bean 交给调用者,调用者管理该 Bean 的生命周期,Spring 不再管理该Bean。

11)如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用 destory() 方法将 Spring 中的 Bean销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。

 

依赖注入

DI是Dependency Injection的缩写,意思是依赖注入,说的是创建对象实例时,同时为这个对象注入它所依赖的属性

分类:一种是调用属性的set方法赋值,第二种使用构造方法赋值

1、set注入指(调用属性的set方法)

2、构造方法注入

spel spring表达式

c、p命名空间

- 基本类型值: p:属性名="值"
- 引用类型值: P:属性名-ref="beanid"

3、复杂类型的注入(Object[],list,set,map,Properties类型)

    <bean id="t1" class="com.vv.bean.Teacher">
        <property name="objects">
            <list>
                <value>vv</value><!--字符串-->
                <value>19</value><!--数-->
                <ref bean="u1"></ref><!--引用类型-->
            </list>
        </property>
        <property name="list">
            <list>
                <value>xx</value><!--字符串-->
                <value>19</value><!--数-->
            </list>
        </property>
        <property name="set">
            <set>
                <value>77</value><!--字符串-->
                <value>19</value><!--数-->
                <ref bean="u1"></ref><!--引用类型-->
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="班长" value="77"></entry>
                <entry key="user" value-ref="u1"></entry><!--引用类型-->
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="username">root</prop>
                <prop key="password">123</prop>
            </props>
        </property>
    </bean>

4、自动装配(注入):Spring会在上下文中自动寻找,并自动给bean装配属性

autowire:

no 不自动装配(默认值)

byName 属性名=id ,调取set方法赋值  

byType 属性的类型和id对象的类型相同,当找到多个同类型的对象时报错,调取set方法赋值(保证id唯一)

constructor 构造方法的参数类型和id对象的类型相同,当没有找到时报错。调取构造方法赋值(保证class唯一)

 

注解实现IOC

1、配置文件中添加约束

xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd

2、配置注解扫描:指定扫描包下所有类中的注解,扫描包时,会扫描包所有的子孙包

<!--扫描包设置--> 
<context:component-scan base-package="com.vv"></context:component-scan>

3、注解

(1)添加到类名上

@Component("对象名") :将某个类注册到Sping中,装配Bean
componet的衍生注解:
    @Service("对象名") // service层 
    @Controller("对象名") // controller层 
    @Repository("对象名") // dao层 

@Scope(scopeName="singleton") //单例对象 
@Scope(scopeName="prototype") //多例对象

(2)添加到属性

@Value("属性值") 
private String name;

@Autowired//默认使用byType,如果通过Repository注解了两个相同类型的对象就会报错
@Qualifier("uDao")//这样就可以指定对应的对象了
private UserDao userDao;

//@Resource(name="对象名") == @Autowired + @Qualifier("name")
@Resource(name="uDao")
private UserDao userDao;

注意:通过注解配置,对象里面的属性就不需要有set方法了

可能会没有Resource这个注解,需要加入依赖包

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.1</version>
</dependency>

(3)添加到方法上

@PostConstruct //等价于init-method属性
public void init(){
    System.out.println("初始化方法");
}

@PreDestroy //等价于destroy-method属性
public void destroy(){
    System.out.println("销毁方法");
}

 

完全注解开发(JavaConfig)

1、创建配置类,替代xml配置文件

@Configuration//作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.vv"})//开启组件扫描
public class SpringConfig {

}

2、加载配置类

ApplicationContext app = new AnnotationConfigApplicationContext(SpringConfig.class);

此方法在springboot中使用

 

Spring AOP

AOP(Aspect Oriented Programming)即面向切面编程。即在不改变原程序的基础上为代码段增加新的功能。应用在权限认证、日志、事务

AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。

想象下面的场景,开发中在多个模块间有某段重复的代码,我们通常是怎么处理的?显然,没有人会靠“复制粘贴”吧。在传统的面向过程编程中,我们也会将这段代码,抽象成一个方法,然后在需要的地方分别调用这个方法,这样当这段代码需要修改时,我们只需要改变这个方法就可以了。然而需求总是变化的,有一天,新增了一个需求,需要再多出做修改,我们需要再抽象出一个方法,然后再在需要的地方分别调用这个方法,又或者我们不需要这个方法了,我们还是得删除掉每一处调用该方法的地方。实际上涉及到多个地方具有相同的修改的问题我们都可以通过 AOP 来解决。

 

AOP的实现机制

- JDK 的动态代理:针对实现了接口的类产生代理。InvocationHandler接口

- CGlib 的动态代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强的技术 生成当前类的子类对象,MethodInterceptor接口

JDK动态代理实现

创建接口实现类的代理对象,增强方法

//代理类
public class dai implements InvocationHandler {

    private UserService service;//目标对象

    public dai(UserService service) {
        this.service = service;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //本方法中的其他输出输入增强
        //proxy 代理方法被调用的代理实例
        System.out.println("日志开始");
        //调取真实的方法
        Object invoke = method.invoke(service, args);
        System.out.println("日志结束");
        return invoke;
    }
}
public class Test {
    public static void main(String[] args) {
        UserService service = new UserServiceImpl();

        dai d = new dai(service);
        //生成代理对象,这里不能转换成一个实际的类,必须是接口类型
        UserService us = (UserService) Proxy.newProxyInstance(service.getClass().getClassLoader(), service.getClass().getInterfaces(), d);
        us.insert(new User());
    }
}

CGlib代理

使用JDK创建代理有一个限制,它只能为接口创建代理实例.这一点可以从Proxy的接口方法 newProxyInstance(ClassLoader loader,Class [] interfaces,InvocarionHandler h)中看的很清楚,第二个入参 interfaces就是需要代理实例实现的接口列表.

CGLib采用底层的字节码技术,可以为一个类创建子类,在子类中完成类的增强

1、添加依赖包

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.2.5</version>
</dependency>

2、创建个工具类(代理器)

public class CglibProxy implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("日志开始");
        Object o1 = methodProxy.invokeSuper(o, objects);
        System.out.println("日志结束");
        return o1;
    }
}

3、测试

public class Test {
    public static void main(String[] args) {
        Try t = new Try();
        //创建代理对象
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(t.getClass());
        enhancer.setCallback(new CglibProxy());
        Try t2 = (Try) enhancer.create();
        t2.test();
    }
}

两种代理方式的区别:

1、jdk动态代理生成的代理类和委托类实现了相同的接口;

2、cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;

3、jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

注:spring同时使用了这两种方式,底层会自行判断应该使用哪种

 

Spring中使用AOP

AOP中的术语

(1)连接点:类中可以被增强的方法

(2)切点:实际被真正增强的方法

(3)增强(通知):增强逻辑的部分(日志处理、权限判断)

(4)切面:把增强运用到切点的过程

步骤

1、添加依赖包

<dependency>
    <groupId>aopalliance</groupId>
    <artifactId>aopalliance</artifactId>
    <version>1.0</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-beans</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>

2、创建增强类(本质上就是一个普通类)

public class MyAop {
    //前置增强-调用目标方法之前执行
    public void before(){
        System.out.println("日志开始");
    }

    //后置增强-调用目标方法之后执行
    public void after(){
        System.out.println("日志开始");
    }

    /**
     * @param joinPoint 获取切入点
     */
    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("环绕开始");
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕结束");
    }
    
    //异常增强
    public void ex(){
        System.out.println("异常增强");
    }
    
    //最终增强
    public void end(){
        System.out.println("最终增强");
    }
}

3、添加aop命名空间

xmlns:aop="http://www.springframework.org/schema/aop" 
http://www.springframework.org/schema/aop 
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd

3、配置文件

增强类型:

前置增强:目标方法运行之前调用 aop:before

后置增强(如果出现异常不会调用):在目标方法运行之后调用 aop:after-returning

环绕增强:在目标方法之前和之后都调用 aop:around

异常增强:程序出现异常时执行(要求:程序代码中不要处理异常) aop:after-throwing

最终增强(无论是否出现 异常都会调用):在目标方法运行之后调用 aop:after

<bean id="udao" class="com.vv.dao.impl.UserDaoImpl"></bean>
<bean id="uService" class="com.vv.service.impl.UserServiceImpl" >
    <property name="userDao" ref="udao"></property>
</bean>

<!--创建增强类对象-->
<bean id="myaop" class="com.vv.util.MyAop"></bean>
<!--建立增强类和目标方法的关系-->
<aop:config>
    <aop:pointcut id="mypc" expression="execution(void com.vv.service.UserService.insert())"/>
    <aop:aspect ref="myaop">
        <!--前置增强-->
        <aop:before method="before" pointcut-ref="mypc"></aop:before>
        <!--后置增强-->
        <aop:after method="after" pointcut-ref="mypc"></aop:after>
        <!--环绕增强-->
        <aop:around method="around" pointcut-ref="mypc"></aop:around>
        <!--异常增强-->
        <aop:after-throwing method="ex" pointcut-ref="mypc"></aop:after-throwing>
        <!--最终增强-->
        <aop:after-returning method="end" pointcut-ref="mypc"></aop:after-returning>
    </aop:aspect>
</aop:config>

4、测试

ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService service = app.getBean("uService", UserService.class);
service.insert();

注意:(1)环绕增强需要使用ProceedingJoinPoint 作为参数(2)注意标签顺序

 

另一种方式

特殊的前置增强-->Advisor前置增强实现

(1)创建增强类,要求该类实现MethodBeforeAdvice接口

(2)设置配置文件,定义增强和切入点的关系

public class agency implements MethodBeforeAdvice {
    /**
     * @param method    要执行目标对象的方法
     * @param objects   参数
     * @param o         目标对象
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+" 的 "+method.getName()+" 被执行了");
    }
}
<bean id="userDao" class="com.vv.dao.Impl.UserDaoImpl"></bean>
<bean id="agency" class="com.vv.agency"></bean>
<aop:config>
    <!--切入点-->
    <aop:pointcut id="pointcut" expression="execution(* com.vv.dao.Impl.*.*(..))"/>
    <aop:advisor advice-ref="agency" pointcut-ref="pointcut"/>
</aop:config>

切入点方法的定义

表达式匹配规则举例:

1、public * addUser(com.pb.entity.User):“*”表示匹配所有类型的返回值。

public int addUser(User u); 
public String addUser(User u);

2、public void *(com.pb.entity.User):“*”表示匹配所有方法名

public void selectUser(User u); 
public void a(User u);

3、public void addUser (..):“..”表示匹配所有参数个数和类型。

public void addUser(int a); 
public void addUser(int b,int c);

4、com.pb.service.*.*(..):匹配com.pb.service 包下所有类的所有方法。

public void com.pb.service.A.a(); 
public String com.pb.service.B.a();

5、* com.pb.service..*(..):匹配com.pb.service 包及子包下所有类的所有方法

 

 

获取切入点信息

通过JoinPoint对象获取信息

public void before(JoinPoint joinPoint){
    System.out.println("日志开始");
    System.out.println(new Date());
    System.out.println("对象信息:"+joinPoint.getTarget().getClass().getSimpleName());
    System.out.println("方法信息:"+joinPoint.getSignature());
    System.out.println("参数信息:"+joinPoint.getArgs());
}

 

使用AspectJ注解开发

1、启动扫描Spring注解的包,启动aspectJ的注解方式

<!--扫描Spring注解包-->
<context:component-scan base-package="com.vv"></context:component-scan>
<!--开启aspectj,生成代理对象-->
<aop:aspectj-autoproxy/>

2、增强类使用@Componet注解,添加@Aspect

3、在增强类中定义切点,指明要修饰的方法

@Component
@Aspect    //生成代理对象
public class MyAop {

    //定义切点,由于注解必须修饰方法,所以这里要定义一个空方法
    @Pointcut("execution(* com.vv.service.*.*(..))")
    public void p(){}

    //前置增强-调用目标方法之前执行
    @Before("p()")
    public void before(){
        System.out.println("日志开始");
    }

    //后置增强-调用目标方法之后执行
    @AfterReturning("p()")
    public void after(){
        System.out.println("日志结束");
    }

    /**
     * @param joinPoint 获取切入点
     */
    @Around("p()")
    public void around(ProceedingJoinPoint joinPoint){
        System.out.println("环绕开始");
        try {
            joinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("环绕结束");
    }

    //异常增强
    @AfterThrowing("p()")
    public void ex(){
        System.out.println("异常增强");
    }

    //最终增强
    @After("p()")
    public void end(){
        System.out.println("最终增强");
    }
}

或者直接这样使用

注解方式中注解的顺序

 

Spring-JDBC 操作数据库

导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.46</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.8.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.mchange</groupId>
    <artifactId>c3p0</artifactId>
    <version>0.9.5.2</version>
</dependency>

原始的使用方式:

public class demo1 {
    public static void main(String[] args) throws PropertyVetoException {
        //1.创建c3p0链接池
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/try");
        dataSource.setUser("root");
        dataSource.setPassword("");
        //创建jdbcTemplate对象
        JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
        //创建sql语句
        String sql="insert into users values (?,?,?);";
        jdbcTemplate.update(sql, 4, "vv", "321");
    }
}

Spring管理jdbcTemplate

让dao实现类继承JdbcDaoSupport,父类中可以提供jdbcTemplate对象

然后修改配置文件,在配置文件中配置数据源信息

<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
    <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/try"/>
    <property name="user" value="root"/>
    <property name="password" value=""/>
</bean>

<bean id="userDao" class="com.vv.dao.Impl.UserDaoImpl">
    <property name="dataSource" ref="ds"></property>
</bean>

dao实现类

public class UserDaoImpl extends JdbcDaoSupport implements UserDao {
    @Override
    public int insert(User user) {
        String sql = "insert into users values (?,?,?);";
        int usid = user.getUsid();
        String username = user.getUsername();
        String password = user.getPassword();
        int update = getJdbcTemplate().update(sql, usid, username, password);
        return update;
    }
}

之前我们是吧数据库的配置信息放在properties文件中,我们这里也可以用同样的方式

jdbc.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/try
username=root
password=

配置文件

<context:property-placeholder location="classpath:jdbc.properties" system-properties-mode="FALLBACK"></context:property-placeholder>

<bean id="ds" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="${driver}"/>
    <property name="jdbcUrl" value="${url}"/>
    <property name="user" value="${username}"/><!--这里的username默认调用的系统的username,在上面添加system-properties-mode="FALLBACK-->
    <property name="password" value="${password}"/>
</bean>

<bean id="userDao" class="com.vv.dao.Impl.UserDaoImpl">
    <property name="dataSource" ref="ds"></property>
</bean>

CRUD操作操作

public class UserDaoImpl extends JdbcDaoSupport implements UserDao {
    @Override
    public int insert(User user) {
        String sql = "insert into users values (?,?,?);";
        int usid = user.getUsid();
        String username = user.getUsername();
        String password = user.getPassword();
        int update = getJdbcTemplate().update(sql, usid, username, password);
        return update;
    }

    @Override
    public User findbyid(int id) {
        String sql ="select * from users where usid = ?";
        User user = getJdbcTemplate().queryForObject(sql, new Object[]{id}, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet resultSet, int i) throws SQLException {
                User user = new User();
                user.setUsid(resultSet.getInt("usid"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        });
        return user;
    }

    @Override
    public List<User> findAll() {
        String sql = "select * from users";
        List<User> list = getJdbcTemplate().query(sql, new RowMapper<User>() {
            @Override
            public User mapRow(ResultSet resultSet, int i) throws SQLException {
                User user = new User();
                user.setUsid(resultSet.getInt("usid"));
                user.setUsername(resultSet.getString("username"));
                user.setPassword(resultSet.getString("password"));
                return user;
            }
        });
        return list;
    }

    @Override
    public int update(int id, User user) {
        String sql = "update users set username = ?, password = ? where usid =?";
        int update = getJdbcTemplate().update(sql,user.getUsername(),user.getPassword(),id);
        return update;
    }

    @Override
    public int delete(int id) {
        String sql = "delete from users where usid = ?";
        int update = getJdbcTemplate().update(sql, id);
        return update;
    }
}

 

事务

通过sql将逻辑相关的一组操作绑定在一起,以便服务器 保持数据的完整性(准确性)。

事务通常是以begin transaction开始,以commit或rollback结束。

事务执行的流程:开启事务->执行insert,update,delete->commit/rollback

事务的特性ACID

1 - 原子性(atomicity)

事务是数据库的逻辑工作单位,而且是必须是原子工作单位,对于其数据修改,要么全部执行,要么全部不执行。

2、一致性(consistency)

事务在完成时,必须是所有的数据都保持一致状态。在相关数据库中,所有规则都必须应用于事务的修改,以保持所有数据的完整性。

3、隔离性(isolation)

一个事务的执行不能被其他事务所影响。企业级的数据库每一秒钟都可能应付成千上万的并发访问,因而带来了并发控制的问题。

4、持久性(durability)

一个事务一旦提交,事务的操作便永久性的保存在DB中。即使此时再执行回滚操作也不能撤消所做的更改

 

事务传播行为propagation

 

多事务方法(对数据库数据进行改变)之间进行调用,这个过程中事务是如何进行管理的。

当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。规定了事务方法和事务方法发生嵌套调用时事务如何进行传播例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。

七种传播行为*

事务传播行为失效的情况

spring事务是基于代理来实现的,所以:

(1)private、fifinal、static 方法无法被代理,所以添加事务无效

(2)当绕过代理对象, 直接调用添加事务管理的方法时, 事务管理将无法生效。比如直接new出的对象。

(3)在同一个类下,有2个方法,A、B,A没有事务,B有事务,但是A调用B时,方法B被标记的事务无效。 究其原因,因为此类的调用对象为代理对象,代理方法A调用真正的被代理方法A后,在被代理方法A中才会去调用方法B,此时this对象为被代理的对象,所以是不会通知到代理对象,也就变成了第二种情况,绕过了代理对象。所以无事务隔离级别

 

事务的隔离级别

脏读:读取到一个未提交事务的数据,若事务回滚可能导致数据不一致

不可重复读:一个未提交的事务两次读取的数据不一致

幻读:一个未提交的事务读取到另一个提交事务添加数据

1. Serializable(串行化):可避免脏读、不可重复读,幻读情况的发生。

2. Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。

3. Read committed(读已提交):可避免脏读情况发生。

4. Read uncommitted(读未提交):最低级别,以上情况均无法保证。

 

 

 

事务操作(Spring事务管理

一般添加到JavaEE三层结构里面的Service层

有两种事务管理操作:编程式事务管理(在代码前后添加代码,不常用)和声明式事务管理(常用)

声明式事务管理(底层使用到了AOP):

1、基于注解

修改配置文件

<!--创建事务管理器-->
<bean id="manager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource" ref="ds"/>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="manager"></tx:annotation-driven>

添加注解

注解中可配置的参数

propagation:事务传播行为(默认Propagation.REQUIRED)

isolation:隔离级别

timeout:超时时间(在一定时间不提交就会回滚,默认-1不超速)

readOnly:是否只读(默认false,可以进行增伤改查,true:只能查询)

rollbackFor:回滚

norollbackFor:不回滚

2、基于XML

<!--创建事务管理器--><!--这里id好像必须写成transactionManager-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!--注入数据源-->
    <property name="dataSource" ref="ds"/>
</bean>

<!--配置增强-->
<tx:advice id="txAdvice">
    <tx:attributes>
        <!--哪种规则的方法添加事务-->
        <tx:method name="insert*" propagation="REQUIRED"/><!--以insert开头的方法-->
        <tx:method name="update*" read-only="true"/>
    </tx:attributes>
</tx:advice>
<!--配置切入点和切面-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="mypoint" expression="execution(* com.vv.service.*.*(..))"/>
    <!--切面   把事务通知配置到切点上-->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="mypoint"></aop:advisor>
</aop:config>

 

posted on 2021-02-06 20:58  1763392456  阅读(175)  评论(0编辑  收藏  举报

导航