08_Spring5

1.基础介绍

  1. Spring 有两个核心部分:IOC 和 Aop

    (1)IOC:控制反转,把创建对象过程交给 Spring 进行管理

    (2)Aop:面向切面,不修改源代码进行功能增强

    1. Spring 特点 :

    (1)方便解耦,简化开发

    (2)Aop 编程支持

    (3)方便程序测试

    (4)方便和其他框架进行整合

    (5)方便进行事务操作

    (6)降低 API 开发难度

2.IOC

概念

  • 控制反转,把对象创建和对象的调用过程交给spring进行管理。
  • 目的:降低耦合度。
  • 底层原理:xml,反射,工厂模式
  • Spring提供IOC容器两种实现方式(两个接口)
    • BeanFactory:Spring内部使用的接口,不提倡开发人员使用。特点:加载配置文件时不会创建对象,获取对象时才会创建对象
    • **ApplicationContext:**BeanFactory的子接口,提供了更多更强大的功能,一般由开发人员使用。特点:加载配置文件时会把配置文件里的对象进行创建。
    • ApplicationContext两个常用实现类:
      • FileSystemXmlApplicationContext:绝对路径,从盘符开始算起
      • ClassPathXmlApplicationContext:相对路径,从src开始算起

Bean管理

什么是Bean管理?Bean管理是指两个操作:Spring创建对象 和 Spring注入属性

Bean管理有两种操作方式:基于xml配置文件方式实现 和 基于注解方式实现

基于xml

基于xml方式创建对象
  • 在Spring配置文件中使用bean标签来创建对象
  • bean标签有很多属性,常用属性:
    • id:唯一标识
    • class:类路径
  • 创建对象时,默认执行无参构造函数
<bean id="user" class="User"></bean>
基于xml方式注入属性
  • property中的name是属性名
使用set方法

为类的属性提供set方法

然后在xml配置文件中通过property标签进行属性注入

<!--配置User对象-->
<bean id="user" class="Bean1.User">
    <property name="name" value="ywj"></property>
    <property name="age" value="18"></property>
</bean>

测试

public void test1() {
    ApplicationContext context = new ClassPathXmlApplicationContext("Bean1/bean.xml");
    User user = context.getBean("user", User.class);
    user.add();
    System.out.println(user.getName() + " " + user.getAge());
}
使用有参构造函数

首先提供有参构造方法

然后再xml配置文件中通过constructor-arg标签进行属性注入

<bean id="user" class="Bean1.User">
    <constructor-arg name="name" value="whh"></constructor-arg>
    <constructor-arg name="age" value="20"></constructor-arg>
</bean>

测试同上

p空间注入

首先在xml配置文件中添加p名称空间,并且在bean标签中进行操作

xmlns:p = "http://www.springframework.org/schema/p"
<bean id="user" class="Bean1.User" p:name="wu" p:age="22"></bean>

测试同上

其他属性注入
null值
<!--配置User对象-->
<bean id="user" class="com.oymn.spring5.User">
    <property name="userName"> <null/> </property>
</bean>
特殊符号

需要通过 <![CDATA[值]]> 来表示或者使用转义字符

<!--配置User对象-->
<bean id="user" class="com.oymn.spring5.User">
    <property name="userName">
    	<value><![CDATA[<<南京>>]]></value>
    </property>
</bean>
对象注入

相当于一个对象中引入另一个对象

外部bean

通过ref

Dao

public interface UserDao {
   public void Update();
}

Impl

public class UserImpl implements UserDao{
    @Override
    public void Update() {
        System.out.println("update");
    }

service

public class UserService {
    //创建UserDao对象,并生成set方法
    private  UserDao userDao;

    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    public void use(){
        userDao.Update();
    }
}

xml

<!--    创建service和dao对象 dao是接口,找他的实现类-->
<bean id="userService" class="bean2.UserService">
    <!--        注入dao对象 ref表示创建的id userImpl-->
    <property name="userDao" ref="userImpl"></property>
</bean>
<bean id="userImpl" class="bean2.UserImpl"></bean>

测试

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("bean2/bean.xml");
    UserService userService = context.getBean("userService", UserService.class);
    userService.use();
}
内部bean

不通过ref属性,而是通过嵌套一个bean标签实现

<bean id="userService" class="bean2.UserService">
    <property name="userDao">
        <bean id="userImpl" class="bean2.UserImpl"></bean>
    </property>
</bean>
级联赋值

写法一:也就是上面所说的外部bean,通过ref属性来获取外部bean

写法二:使用.

注:写法二需要emp提供dept的get方法。

<!--级联赋值-->
<bean id="emp" class="com.rokned.spring5.bean.Emp">
    <!--写法一-->
	<property name="dept" ref="dept"></property>
    <!--写法二-->
    <property name="dept.dname" value="技术部"></property>
</bean>
<bean id="dept" class="com.rokned.spring5.bean.Dept">
    <property name="dname" value="财务部"></property>
</bean>

注入集合属性
简单集合
<bean id="stu" class="bean3.Stu">
    <!--数组类型属性注入-->
    <property name="courses">
        <array>
            <value>java课程</value>
            <value>数据库课程</value>
        </array>
    </property>
    <!--List类型属性注入-->
    <property name="list">
        <list>
            <value>张三</value>
            <value>李四</value>
        </list>
    </property>
    <!--Map类型属性注入-->
    <property name="map">
        <map>
            <entry key="JAVA" value="java"></entry>
            <entry key="PHP" value="php"></entry>
        </map>
    </property>
    <!--Set类型属性注入-->
    <property name="set">
        <set>
            <value>Mysql</value>
            <value>Redis</value>
        </set>
    </property>
</bean>
对象集合

集合+外部bean

<!--创建多个 course 对象-->
<bean id="course1" class="com.rokned.spring5.collectiontype.Course">
	<property name="cname" value="Spring5 框架"></property>
</bean>
<bean id="course2" class="com.rokned.spring5.collectiontype.Course">
	<property name="cname" value="MyBatis 框架"></property>
</bean>

<!--注入 list 集合类型,值是对象-->
<property name="courseList">
    <list>
        <ref bean="course1"></ref>
        <ref bean="course2"></ref>
    </list>
</property>
集合复用

使用 util 标签

<!--将集合注入部分提取出来-->
<util:list id="booklist">
    <value>易筋经</value>
    <value>九阳神功</value>
</util:list>

<bean id="book" class="com.oymn.spring5.Book">
    <property name="list" ref="booklist"></property>
</bean>

XML自动装配
  • 根据指定的装配规则(属性名称或者属性类型),Spring自动将匹配的属性值进行注入

  • 根据属性名称自动装配:要求 emp中属性的名称dept 和 bean标签的id值dept 一样,才能识别

    <!--指定autowire属性值为byName-->
    <bean id="emp" class="com.oymn.spring5.Emp" autowire="byName"></bean>
    
    <bean id="dept" class="com.oymn.spring5.Dept"></bean>
    
  • 根据属性类型自动装配:要求同一个xml文件中不能有两个相同类型的bean,否则无法识别是哪一个

    <!--指定autowire属性值为byType-->
    <bean id="emp" class="com.oymn.spring5.Emp" autowire="byType"></bean>
    
    <bean id="dept" class="com.oymn.spring5.Dept"></bean>
    
外部属性文件来操作bean

bean.xml

<!-- 标签头中添加命名空间 -->
xmlns:context = "http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd

<!-- 配置连接池 -->
<!--    引入外部文件-->
<context:property-placeholder location="classpath:bean6/druid.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
    <property name="driverClassName" value="${druid.driverClassName}"></property>
    <property name="url" value="${druid.url}"></property>
    <property name="username" value="${druid.username}"></property>
    <property name="password" value="${druid.password}"></property>
</bean>

druid.properties

#注意一定要带前缀druid,不然会命名冲突报错
druid.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false
druid.username=root
druid.password=123456
druid.driverClassName=com.mysql.jdbc.Driver
initialSize=10
maxActive=20
maxWait=1000
filters=wall

测试

@Test
public void Test1() throws SQLException {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean6/bean.xml");
    DataSource souce = (DataSource)context.getBean("dataSource", DruidDataSource.class);
    Connection conn = souce.getConnection();
    System.out.println(conn );
}
FactoryBean

Spring有两种Bean,一种是普通Bean,另一种是工厂Bean(FactoryBean)

  • 普通bean返回类型就是配置文件中定义类型
  • 工厂bean返回类型可以和配置文件中不一样

创建类,作为工厂bean实现FactoryBean,并实现接口方法中定义的返回bean类型

public class Mybean implements FactoryBean<Person> {
    //定义返回bean
    @Override
    public Person getObject() throws Exception {
        Person p = new Person("芜湖");
        return p;
    }

    @Override
    public Class<?> getObjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return FactoryBean.super.isSingleton();
    }
}

xml

<bean id="mybean" class="bean4.Mybean"></bean>

测试

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("/bean4/bean.xml");
    //        Mybean mybean = context.getBean("mybean", Mybean.class);
    Person mybean = context.getBean("mybean", Person.class);
    System.out.println(mybean);
}
bean作用域
  • 在Spring中,默认情况下bean是单实例对象
  • 通过 bean标签的scope属性 来设置单实例还是多实例。
<!--    <bean id="user" class="bean5.User" p:name="wuhu" p:age="22"></bean>-->
<bean id="user" class="bean5.User" p:name="wuhu" p:age="22" scope="prototype"></bean>
@Test
public void test1() {
    ApplicationContext context = new ClassPathXmlApplicationContext("bean5/bean.xml");
    User user1 = context.getBean("user", User.class);
    User user2 = context.getBean("user", User.class);
    System.out.println(user1);
    System.out.println(user2);
}
bean生命周期
  1. 通过构造器创建 bean 实例(无参数构造)

  2. 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

  3. 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

  4. 调用 bean 的初始化的方法(需要进行配置初始化的方法)

  5. 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

  6. bean 可以使用了(对象获取到了)

  7. 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

public class Orders {
    private String orderName;

    public Orders() {
        System.out.println("第一步:执行无参构造方法创建bean实例");
    }

    public void setOrderName(String orderName) {
        this.orderName = orderName;
        System.out.println("第二步:调用set方法设置属性值");
    }

    //初始化方法,需要xml配置
    public void initMethod(){
        System.out.println("第四步:执行初始化方法");
    }

    //销毁方法,需要xml配置
    public void destroyMethod(){
        System.out.println("第七步:执行销毁方法");
    }
}
//实现后置处理器,需要实现BeanPostProcessor接口
public class MyBeanPost implements BeanPostProcessor {

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第三步:将bean实例传递给bean后置处理器的postProcessBeforeInitialization方法");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("第五步:将bean实例传递给bean后置处理器的postProcessAfterInitialization方法");
        return bean;
    }
}
<bean id="orders" class="com.oymn.spring5.Orders" init-method="initMethod" destroy-method="destroyMethod">
    <property name="orderName" value="hahah"></property>
</bean>

<!--配置bean后置处理器,这样配置后整个xml里面的bean用的都是这个后置处理器-->
<bean id="myBeanPost" class="com.oymn.spring5.MyBeanPost"></bean>
@Test
public void testOrders(){
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
    Orders orders = context.getBean("orders", Orders.class);
    System.out.println("第六步:获取bean实例对象");
    System.out.println(orders);
    //手动让bean实例销毁
    context.close();
}

基于注解

  • 格式:@注解名称(属性名=属性值,属性名=属性值,……)
  • 注解可以作用在类,属性,方法。
  • 使用注解的目的:简化xml配置
创建对象
  • @Component:普通注解
  • @Service:一般用于Service层
  • @Controller:一般用于web层
  • @ Repository:一般用于Dao层
  • 功能一样,都可以创建bean实例。为了更加清晰命名才不同

1.引入依赖

在这里插入图片描述

2.开启组件扫描

扫描base-package包下所有有注解的类并为其创建对象

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

<context:component-scan base-package="Anno.bean1"></context:component-scan>

3.使用注解

//这里通过@Component注解来创建对象,括号中value的值等同于之前xml创建对象使用的id,为了后面使用时通过id来获取对象
//括号中的内容也可以省略,默认是类名并且首字母小写
//可以用其他三个注解
@Component(value="stuService")
public class StuService {
    public void add(){
        System.out.println("addService");
    }
}
属性注入
Autowired

根据属性类型自动装配

Dao

public interface StuDao {
    public void add();
}

Impl

@Repository
public class StuDaoImpl implements StuDao {
    @Override
    public void add() {
        System.out.println("StuDaoImpl");
    }
}

Service

@Component(value="stuService")
public class StuService {

    @Autowired
    public StuDao stuDao;

    public void add(){
        System.out.println("addService");
        stuDao.add();
    }
}

Test

@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("Anno/bean2/bean.xml");
    StuService stuService = context.getBean("stuService", StuService.class);
    System.out.println(stuService);
    stuService.add();
}
Qualifier

根据属性名称自动装配

当遇到一个接口有很多实现类时,比如由stuDaoImpl,stuDaoImpl1,stuDaoImpl2

只通过@Autowire是无法完成自动装配的,所以需要再使用@Qualifier通过名称来锁定某个类

@Component(value="stuService")
public class StuService {

    @Autowired
    @Qualifier(value="stuDaoImpl")  //这样就能显式指定stuDaoImpl这个实现类
    public StuDao stuDao;

    public void add(){
        System.out.println("addService");
        stuDao.add();
    }
}
Resource

可以根据类型注入,也可以根据名称注入

@Component(value="stuService")
public class StuService {

    //@Resource   //根据类型进行注入
    @Resource(name="stuDaoImpl")  //根据名称进行注入
    public StuDao stuDao;

    public void add(){
        System.out.println("addService");
        stuDao.add();
    }
}
Value

注入普通类型属性

@Value(value = "abc")
private String name;
完全注解开发

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

@Configuration    //表明为一个配置类
@ComponentScan(basePackages = "com.oymn")   //开启组件扫描
public class SpringConfig {
}
@Test
public void test2(){
    //创建AnnotationConfigApplicationContext对象
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    StuService stuService = context.getBean("stuService", StuService.class);
    System.out.println(stuService);
    stuService.add();
}
扫描细节配置
<!--use-default-fileters设置为false表示不使用默认过滤器,使用自定义扫描方法,通过include-filter来设置只扫描com.oymn包下的所有@Controller修饰的类 -->
<context:component-scan base-package="com.oymn" use-default-filters="false">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

<!--exclude-filter设置哪些注解不被扫描,例子中为@Controller修饰的类不被扫描-->
<context:component-scan base-package="com.oymn">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

3.AOP

  • 面向切面编程,利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。通俗来说就是在不修改代码的情况下添加新的功能。
  • 底层通过动态代理来实现:
    • 第一种:有接口的情况,使用JDK动态代理:创建接口实现类的代理对象在这里插入图片描述

    • 第二种:无接口的情况,使用CGLIB动态代理:创建当前类子类的代理对象在这里插入图片描述

JDK动态代理

基础见java反射

public interface UserDao {
    public int add(int a, int b);
    public int multi(int a, int b);
}
public class UserDaoImpl implements UserDao {
    @Override
    public int add(int a, int b) {
        return a+b;
    }

    @Override
    public int multi(int a, int b) {
        return a*b;
    }
}
public class Main {

    @Test
    public void test1() {

        //所需代理的类实现的接口,支持多个接口
        Class[] interfaces = {UserDao.class};

        UserDao userDao = new UserDaoImpl();

        //调用newProxyInstance方法来创建代理类
        UserDao userDaoProxy = (UserDao) Proxy.newProxyInstance(Main.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));

        int result = userDaoProxy.add(1, 2);
        System.out.println(result);
    }

    //创建内部类,实现InvocationHandler接口,重写invoke方法,添加新功能
    class UserDaoProxy implements InvocationHandler {

        Object obj;

        //通过有参构造函数将所需代理的类传过来
        public UserDaoProxy(Object obj) {
            this.obj = obj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            System.out.println("进入" + method.getName() + "方法,这是新增的代码,参数有" + Arrays.toString(args));

            //执行原有的代码
            Object invoke = method.invoke(obj, args);

            System.out.println("方法原先的内容执行完了");

            return invoke;
        }
    }
}

AOP相关术语

  • 连接点:类中可以被增强的方法,称为连接点。
  • 切入点:实际被增强的方法,称为切入点。
  • 通知:增强的那一部分逻辑代码。通知有多种类型:
    • 前置通知:增强部分代码在原代码前面。
    • 后置通知:增强部分代码在原代码后面。
    • 环绕通知:增强部分代码既有在原代码前面,也有在原代码后面。
    • 异常通知:原代码发生异常后才会执行。
    • 最终通知:类似与finally那一部分
  • 切面:指把通知应用到切入点这一个动作。

基于AspectJ实现AOP

  • 基于xml配置文件
  • 基于注解方法

切入点表达式

  • 语法:execution([权限修饰符] [返回类型] [类全路径] [方法名称] [参数列表])

  • *注:权限修饰符可以不写,但是返回类型必须要写,表示所有返回类型

    @Before(value="execution(void AspectJAnno.User1.add(..))")
    
  • 举例1:对 com.rokned.dao.BookDao 类里面的 add 进行增强

    execution(* com.auguigu.dao.BookDao.add(..))
    
  • 举例2:对 com.rokned.dao.BookDao 类里面的所有的方法进行增强

    execution(* com.rokned.dao.BookDao.*(..))
    
  • 举例 3:对 com.rokned.dao 包里面所有类,类里面所有方法进行增强

    execution(* com.rokned.dao.*.* (..))
    

基于注解方法

1.引入依赖

2.创建类和增强类

@Component
public class User {
    public void add(){
        System.out.println("User.add()");
    }
}
@Component
@Aspect   //使用Aspect注解
public class UserProxy {
    //前置通知
    @Before(value="execution(* com.oymn.spring5.User.add(..))")
    public void before(){
        System.out.println("UserProxy.before()");
    }

    //后置通知
    @AfterReturning(value="execution(* com.oymn.spring5.User.add(..))")
    public void afterReturning(){
        System.out.println("UserProxy.afterReturning()");
    }

    //最终通知
    @After(value="execution(* com.oymn.spring5.User.add(..))")
    public void After(){
        System.out.println("UserProxy.After()");
    }

    //异常通知
    @AfterThrowing(value="execution(* com.oymn.spring5.User.add(..))")
    public void AfterThrowing(){
        System.out.println("UserProxy.AfterThrowing()");
    }

    //环绕通知
    @Around(value="execution(* com.oymn.spring5.User.add(..))")
    public void Around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{

        System.out.println("UserProxy.Around()   _1");

        //调用proceed方法执行原先部分的代码
        proceedingJoinPoint.proceed();

        System.out.println("UserProxy.Around()   _2");
    }
}

3.配置文件开启组件扫描和生成代理对象(可以不使用xml直接配置类)

xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
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

<!--开启组件扫描-->
<context:component-scan base-package="AspectJ"></context:component-scan>
<!--开启AspectJ生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

3.配置类

@Configuration
@ComponentScan(basePackages = "AspectJAnno")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringConfig {
}

在这里插入图片描述

在这里插入图片描述

注:出现异常时,After最终通知有执行,而AfterReturning后置通知并没有执行,环绕后置也没执行。

公共切入点抽取

通过@Pointcut注解

@Pointcut(value="execution(* com.oymn.spring5.User.add(..))")
public void pointDemo(){

}

//前置通知
@Before(value="pointDemo()")
public void before(){
    System.out.println("UserProxy.before()");
}

增强级别

当有多个增强类对同一方法进行增强时,可以通过**@Order(数字值)来设置增强类的优先级,数字越小优先级越高。

@Component
@Aspect
@Order(1)
public class PersonProxy

基于xml

基本不用,了解即可

public class Book {
    public void buy(){
        System.out.println("buy()");
    }
}
public class BookProxy {
    public void before(){
        System.out.println("before()");
    }
}
<!--创建对象-->
<bean id="book" class="AspectJxml.Book"></bean>
<bean id="bookProxy" class="AspectJxml.BookProxy"></bean>

<aop:config>
    <!--切入点-->
    <aop:pointcut id="p" expression="execution(* AspectJxml.Book.buy(..))"/>
    <!--配置切面-->
    <aop:aspect ref="bookProxy">
        <aop:before method="before" pointcut-ref="p"/>  <!--将bookProxy中的before方法配置为切入点的前置通知-->
    </aop:aspect>
</aop:config>
@Test
public void test1(){
    ApplicationContext context = new ClassPathXmlApplicationContext("AspectJxml/bean.xml");
    Book book = context.getBean("book", Book.class);
    book.buy();
}

4.JdbcTemplate

  • Spring对JDBC进行封装,使用JdbcTemplate方便对数据库的操作。

1.引入相关jar包

2.配置数据库连接池;配置JdbcTemplate对象

<context:component-scan base-package="*"></context:component-scan>
<context:property-placeholder location="classpath:druid.properties"></context:property-placeholder>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <!-- 获取properties文件内容,根据key获取,使用spring表达式获取 -->
    <property name="driverClassName" value="${druid.driverClassName}"></property>
    <property name="url" value="${druid.url}"></property>
    <property name="username" value="${druid.username}"></property>
    <property name="password" value="${druid.password}"></property>
</bean>

<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入数据库连接池-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

3.创建Service类和Dao类,在Dao类中注入JdbcTemplate对象

@Repository
public class BookDaoImpl implements BookDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;
}
public class BookService {
    @Autowired
    private BookDao bookDao = new BookDaoImpl();
}

在这里插入图片描述

@Repository
public class BookDaoImpl implements BookDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    @Override
    public void add(Book book) {
        String sql = "insert into t_book set name=?,price=?";
        Object[] args = {book.getBookName(),book.getBookPrice()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }

    @Override
    public void update(Book book) {
        String sql = "update t_book set name=?,price=? where id=?";
        Object[] args = {book.getBookName(),book.getBookPrice(),book.getBookId()};
        int update = jdbcTemplate.update(sql, args);
        System.out.println(update);
    }

    @Override
    public void delete(int id) {
        String sql = "delete from t_book where id=?";
        int update = jdbcTemplate.update(sql, id);
        System.out.println(update);
    }

    @Override
    public int queryCount() {
        String sql = "select count(*) from t_book";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        return count;
    }

    @Override
    public Book queryBookById(int id) {
        String sql = "select id bookId,name bookName,price bookPrice from t_book where id=?";
        Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
        return book;
    }

    @Override
    public List<Book> queryBooks() {
        String sql = "select id bookId,name bookName,price bookPrice from t_book";
        List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
        return bookList;
    }

    @Override
    public void batchAddBook(List<Object[]> books) {
        String sql = "insert into t_book set id=?,name=?,price=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, books);
        System.out.println(ints);
    }

    @Override
    public void batchUpdateBook(List<Object[]> books) {
        String sql = "update t_book set name=?,price=? where id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, books);
        System.out.println(ints);
    }

    @Override
    public void batchDeleteBook(List<Object[]> args) {
        String sql = "delete from t_book where id=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, args);
        System.out.println(ints);
    }
}

5.事务管理

  • Spring事务管理有两种方式:编程式事务管理(手动写代码管理) 和 声明式事务管理,一般使用声明式事务管理,底层使用AOP原理。

  • 声明式事务管理有两种方式:基于xml配置方式 和 基于注解方式,一般使用注解方式。

  • Spring事务管理提供了一个接口,叫做事务管理器,这个接口针对不同的框架提供不同的实现类。

对于使用JdbcTemplate进行数据库交互,则使用DataSourceTransactionManager实现类,如果整合Hibernate框架则使用HibernateTransactionManager实现类,具体情况具体使用。

注解实现声明式事务管理

1.配置文件

<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
      destroy-method="close">
    <property name="url" value="jdbc:mysql://localhost:3306/book" />
    <property name="username" value="root" />
    <property name="password" value="000000" />
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>

<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入数据库连接池-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

在service类上面或者service类的方法上面添加事务注解@Transactional

  • 如果把@Transactional添加在类上面,这个类里面所有方法都添加事务。
  • 如果只是添加在方法上面,则只为这个方法添加事务。
@Service
@Transactional
public class UserService {}

声明式事务管理的参数配置

  1. propagation:事务传播行为,多事务方法进行调用,这个过程中事物是如何进行管理的,总共有7种在这里插入图片描述

  2. isolation:事务隔离级别在这里插入图片描述

  3. timeout:超时时间

    • 事务需要在一定时间内进行提交,超过时间后回滚。
    • 默认值是-1,设置时间以秒为单位。
  4. readOnly:是否只读

    • 默认值为false,表示可以查询,也可以增删改。
    • 设置为true,只能查询。
  5. rollbackFor:回滚,设置出现哪些异常进行事务回滚。

  6. noRollbackFor:不回滚,设置出现哪些异常不进行事务回滚。

@Service
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.READ_COMMITTED)
public class AccountService {}

完全注解实现声明式事务管理

@Configuration  //配置类
@ComponentScan(basePackages = "com.oymn.spring5")  //开启组件扫描
@EnableTransactionManagement  //开启事务
public class Config {

    //创建数据库连接池
    @Bean
    //在IOC中创建了
    public DruidDataSource getDruidDataSource(){
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        druidDataSource.setUrl("jdbc:mysql://localhost:3306/book");
        druidDataSource.setUsername("root");
        druidDataSource.setPassword("000000");
        return druidDataSource;
    }
    //创建JdbcTemplate对象
    @Bean
    //在IOC容器中存在了dataSource,所以在IOC中寻找
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }
    //创建事务管理器
    @Bean
    //同理,IOC中寻找
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

xml实现事务管理

<context:component-scan base-package="com.oymn"></context:component-scan>

<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
      destroy-method="close">
    <property name="url" value="jdbc:mysql://localhost:3306/book" />
    <property name="username" value="root" />
    <property name="password" value="000000" />
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>

<!--创建JdbcTemplate对象-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!--注入数据库连接池-->
    <property name="dataSource" ref="dataSource"></property>
</bean>

<!--创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--配置事务通知-->
<tx:advice id="txadvice">
    <!--配置事务参数-->
    <tx:attributes>
        <tx:method name="accountMoney" propagation="REQUIRED" />
    </tx:attributes>
</tx:advice>

<!--配置切入点和切面-->
<aop:config>
    <!--配置切入点-->
    <aop:pointcut id="pt" expression="execution(* com.oymn.spring5.Service.*.*(..))"/>
    <!--配置切面-->
    <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>

6.引入log4j2

1.引入依赖

在这里插入图片描述

2.log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的appender-->
    <appenders>
        <!--输出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>

7.其他

Nullable注解

@Nullable注解可以用在方法上,属性上,参数上,表示方法返回值可以为空,属性可以为空,参数可以为空。

@Nullable     //表示方法返回值可以为空
public int getId();

@Nullable     //表示参数可以为空
public void setId(@Nullable int Id);

@Nullable     //表示属性可以为空
public int id;

函数式编程

这是因为java8新增了lamda表达式

@Test
public void test() {
    //1 创建 GenericApplicationContext 对象
    GenericApplicationContext context = new GenericApplicationContext();
    //2 调用 context 的方法对象注册
    context.refresh();
    context.registerBean("user1",User.class,() -> new User());
    //3 获取在 spring 注册的对象
    // User user = (User)context.getBean("com.rokned.spring5.test.User");
    User user = (User)context.getBean("user1");
    System.out.println(user);
}

整合Junit

Junit4

1.引入依赖

2.配置测试类框架

@RunWith(SpringJUnit4ClassRunner.class) //单元测试框架
@ContextConfiguration("classpath:bean4.xml") //加载配置文件,使用注解,不用手动写
public class JUnitTest {

    @Autowired	//自动注入,不用每次都使用context获取
    public User user;

    @Test
    public void test(){
        System.out.println(user);
    }
}

Junit5

1.引入依赖

2.配置测试类框架和上面类似将注解换成Junit5的

在这里插入图片描述

框架注解二合一

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值