Spring

概述

Maven引入

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.3.3</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.3.3</version>
</dependency>

HelloSpring

实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    private Integer id;
    private String username;
    private String password;
}

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"> 
    <!--bean就是java对象 , 由Spring创建和管理-->
    <!--id为获取唯一标识-->
    <bean id="user" class="com.demo.entity.User">
        <property name="id" value="1"/>
        <property name="username" value="admin"/>
        <property name="password" value="admin"/>
    </bean>
</beans>

测试

@Test
public void hello() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//    User user = context.getBean("user", User.class);
    User user = (User) context.getBean("user");
    System.out.println(user);
}

创建对象的方式

在配置文件加载的时候。其中管理的对象都已经初始化了

set注入

创建对象:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
    private String name;
    private Teacher teacher;
    private String[] books;
    private List<String> hobbies;
    private Map<String, String> cards;
    private Set<String> games;
    private String wife;
    private Properties infos;
}

常量注入

<bean id="teacher" class="com.demo.entity.Teacher">
    <property name="name" value="李老师"/>
</bean>

bean注入

<bean id="student" class="com.demo.entity.Student">
    <property name="name" value="张三"/>
    <!--引用类型用ref,引用Spring容器里的id-->
    <property name="teacher" ref="teacher"/>
</bean>

数组注入

<bean id="student" class="com.demo.entity.Student">
    <property name="name" value="张三"/>
    <property name="teacher" ref="teacher"/>
    <property name="books">
        <array>
            <value>西游记</value>
            <value>红楼梦</value>
            <value>水浒传</value>
        </array>
    </property>
</bean>

List注入

<property name="hobbies">
    <list>
        <value>听歌</value>
        <value>看电影</value>
        <value>爬山</value>
    </list>
</property>

Map注入

<property name="cards">
    <map>
        <entry key="工商银行" value="123"/>
        <entry key="农业银行" value="456"/>
    </map>
</property>

Set注入

<property name="games">
    <set>
        <value>魔兽世界</value>
        <value>星际争霸</value>
        <value>英雄联盟</value>
    </set>
</property>

Null注入

<property name="wife"><null/></property>

Properties注入

<property name="infos">
    <props>
        <prop key="age">14</prop>
        <prop key="gender">男</prop>
        <prop key="address">北京</prop>
    </props>
</property>

构造器注入

<!--构造器index下标-->
<bean id="user1" class="com.demo.entity.User">
    <!--从0开始-->
    <constructor-arg index="0" value="1"/>
    <constructor-arg index="1" value="admin1"/>
    <constructor-arg index="2" value="123"/>
</bean>
<!--构造器参数名字-->
<bean id="user2" class="com.demo.entity.User">
    <constructor-arg name="id" value="2"/>
    <constructor-arg name="username" value="admin2"/>
    <constructor-arg name="password" value="123"/>
</bean>
<!--构造器参数类型-->
<bean id="user3" class="com.demo.entity.User">
    <constructor-arg type="java.lang.Integer" value="3"/>
    <!--参数类型相同时按顺序赋值-->
    <constructor-arg type="java.lang.String" value="admin3"/>
    <constructor-arg type="java.lang.String" value="123"/>
</bean>

扩展注入

P命名空间注入

导入约束 : xmlns:p="http://www.springframework.org/schema/p" 

<!--P(属性: properties)命名空间 , 属性依然要设置set方法--> 
<bean id="user" class="com.kuang.pojo.User" p:name="张三" p:age="18"/>

C命名空间注入

导入约束 : xmlns:c="http://www.springframework.org/schema/c" 

<!--C(构造: Constructor)命名空间 , 属性依然要设置set方法--> 
<bean id="user" class="com.kuang.pojo.User" c:name="张三" c:age="18"/>

Spring配置

别名

<alias name="user" alias="userAlias"/>

Bean的配置

别名

<!--id唯一标识符,name别名-->
<bean id="user" name="u1 u2,u3;u4" class="com.demo.entity.User">
    <property name="id" value="1"/>
    <property name="username" value="admin"/>
    <property name="password" value="admin"/>
</bean>

作用域

<bean id="user" class="xxx.xx" scope="作用域">
    <property name="id" value="1"/>
</bean>

import

导入其他Spring配置文件,合并为一个配置文件

<import resource="bean1.xml"/>
<import resource="bean2.xml"/>

Bean的自动装配

  • 自动装配是使用spring满足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;
  2. 在java中显式配置;
  3. 隐式的bean发现机制和自动装配。

byName

<bean id="teacher" class="com.demo.entity.Teacher">
    <property name="name" value="李老师"/>
</bean>
<bean id="stu" class="com.demo.entity.Student" autowire="byName">
    <property name="name" value="张三"/>
</bean>

当一个bean节点带有 autowire byName 的属性时

  1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
  2. 去spring容器中寻找是否有此字符串名称id的对象。
  3. 如果有,就取出注入;如果没有,就报空指针异常。

byType

<bean class="com.demo.entity.Teacher">
    <property name="name" value="李老师"/>
</bean>
<bean id="stu" class="com.demo.entity.Student" autowire="byType">
    <property name="name" value="张三"/>
</bean>

使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。

使用注解

准备工作:

  1. 在spring配置文件中引入context文件头
xmlns:context="http://www.springframework.org/schema/context"

http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
  1. 开启属性注解支持
<context:annotation-config/>

@Autowired

  • @Autowired是按类型自动装配的,不支持id匹配
  • 有多个同类型时尝试按set id byName匹配,没有则报错

@Autowired(required = false) // required = false 表示对象可以为null
private Teacher teacher;

@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用
@Autowired()
@Qualifier("teacher1") // 指定id
private Teacher teacher;

@Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配;
  • 都不成功(多个同类型),则报异常。
@Resource(name = "teacher1")
private Teacher teacher;

使用注解开发

在spring4之后,想要使用注解形式,必须得要引入aop的包

引入context约束:

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

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

开启包扫描

<!--开启注解支持-->
<context:annotation-config/>
<!--开启包扫描,让指定包下的注解生效,由IOC容器统一管理-->
<context:component-scan base-package="com.demo.entity"/>

bean注入

@Component

@Component("teacher1") // 等价于 <bean id="teacher1" class="com.demo.entity.Teacher"/>,默认id为类名首字母小写
public class Teacher {
    private String name;
}

@Repository

dao层

@Controller

web层

@Service

service层

属性注入

@Value

@Value("李老师")
private String name;

@Autowired

@Scope作用域

@Component
@Scope("prototype")
public class Teacher {
}

基于Java类进行配置

可以省去配置文件

新建类

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
// 不自动纳入容器
public class Dog {
    private String name;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component // 自动纳入Spring容器
public class Teacher {
    @Value("李老师")
    private String name;
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component // 自动纳入Spring容器
public class Student {
    @Value("张三")
    private String name;
    @Resource
    private Teacher teacher;
}

新建配置类

@Configuration // 代表这是一个配置类
@ComponentScan("com.demo.entity") // 扫描其他包下的自动Bean
@Import(MyConfig.class) //导入合并其他配置类,类似于配置文件中的 import 标签
public class AppConfig {

    @Bean// 通过方法手动注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id
    public Dog dog() {
        return Dog.builder().name("旺财").build();
    }

    @Bean("dog1")// 也可以自定义bean名
    public Dog dog() {
        return Dog.builder().name("旺财1").build();
    }
}

测试

@Test
public void test() {
    // 从配置类获取上下文环境
    ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    Dog dog = context.getBean("dog", Dog.class);
    System.out.println(dog);
    Teacher teacher = context.getBean("teacher", Teacher.class);
    System.out.println(teacher);
    Student student = context.getBean("student", Student.class);
    System.out.println(student);
}

代理模式

静态代理

租房案例

Rent接口:

public interface Rent {
    /**
     * 出租房子
     */
    void rent();
}

房东:

public class Host implements Rent{
    /**
     * 出租房子
     */
    @Override
    public void rent() {
        System.out.println("房东出租房子");
    }
}

中介:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Proxy implements Rent {
    private Host host;

    /**
     * 出租房子
     */
    @Override
    public void rent() {
        seeHouse();
        host.rent();
    }

    public void seeHouse() {
        System.out.println("中介带你看房");
    }
}

客户:

public class Client {
    public static void main(String[] args) {
        Host host = new Host();
//        host.rent();
        Proxy proxy = new Proxy(host);
        proxy.rent();
    }
}

新增日志案例

UserService:

public interface UserService {
    void getUser();
    void addUser();
}

UserServiceImpl:

public class UserServiceImpl implements UserService{
    
    @Override
    public void getUser() {
        System.out.println("获取用户");
    }

    @Override
    public void addUser() {
        System.out.println("新增用户");
    }
}

UserServiceProxy:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserServiceProxy {
    private UserService userService;

    public void getUser() {
        log("使用了getUser方法");
        userService.getUser();
    }
    public void addUser() {
        log("使用了addUser方法");
        userService.addUser();
    }

    public void log(String msg) {
        System.out.println(msg);
    }
}

Client:

public class Client {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
//        userService.getUser();
//        userService.addUser();
        UserServiceProxy userServiceProxy = new UserServiceProxy(userService);
        userServiceProxy.getUser();
        userServiceProxy.addUser();
    }
}

动态代理

  • 动态代理的角色和静态代理的一样 .
  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的
  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理
    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理--cglib
    • 现在用的比较多的是 javasist 来生成动态代理 .

JDK的动态代理需要了解两个类

  • 核心:InvocationHandlerProxy

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!

处理类

public class ProxyInvocationHandler implements InvocationHandler {
    // 被代理的接口
    private Object target;

    public ProxyInvocationHandler(Object target) {
        this.target = target;
    }

    // 生成得到代理类
    public Object getProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    // 处理代理实例,并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("中介带你看房");
        Object result = method.invoke(target, args);
        System.out.println("签订合同");
        return result;
    }
}

动态生成代理对象

public class Client2 {
    public static void main(String[] args) {
        // 真实角色
        Host host = new Host();

        // 设置要代理的对象
        ProxyInvocationHandler pih = new ProxyInvocationHandler(host);

        // 生成代理角色
        Rent rent = (Rent) pih.getProxy();
        rent.rent();
    }
}

AOP

需要导入一个包

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.6</version>
</dependency>

通过 Spring API 实现

AOP的约束

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

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

新建Active类

public class BeforeLog implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName() + "的" + method.getName() + "执行前");
    }
}
public class AfterLog implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了" + method.getName() + ",返回结果为:" + returnValue);
    }
}

配置文件配置

<!--注册bean-->
<bean id="userService" class="com.demo.service.UserServiceImpl"/>
<bean id="beforeLog" class="com.demo.log.BeforeLog"/>
<bean id="afterLog" class="com.demo.log.AfterLog"/>

<!--配置aop-->
<!--方式一:使用原生Spring API接口-->
<aop:config>
    <!--配置切入点-->
    <aop:pointcut id="pointcut" expression="execution(* com.demo.service.UserServiceImpl.*(..))"/>
    <!--配置环绕-->
    <aop:advisor advice-ref="beforeLog" pointcut-ref="pointcut"/>
    <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
</aop:config>

测试

public class ClientAop {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 动态代理代理的是接口
        UserService userService = context.getBean("userService", UserService.class);
        userService.getUser();
        userService.addUser();
    }
}

自定义类

新建切面类

public class MyAspect {
    public void before() {
        System.out.println("===方法执行前===");
    }
    public void after() {
        System.out.println("===方法执行后===");
    }
}

配置文件配置

<!--方式二:自定义切面-->
<!--引入自定义切面类-->
<bean id="myAspect" class="com.demo.advice.MyAspect"/>
<aop:config>
    <!--切面配置-->
    <aop:aspect ref="myAspect">
        <!--切入点配置-->
        <aop:pointcut id="pointcut" expression="execution(* com.demo.service.UserServiceImpl.*(..))"/>
        <!--环绕配置-->
        <aop:before method="before" pointcut-ref="pointcut"/>
        <aop:after method="after" pointcut-ref="pointcut"/>
    </aop:aspect>
</aop:config>

测试

使用注解实现

新建切面类

@Aspect
public class AnnotationAspect {
    @Before("execution(* com.demo.service.UserServiceImpl.*(..))")
    public void before() {
        System.out.println("===AnnotationAspect执行前===");
    }
    @After("execution(* com.demo.service.UserServiceImpl.*(..))")
    public void after() {
        System.out.println("===AnnotationAspect执行后===");
    }
    @Around("execution(* com.demo.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable {
        System.out.println("环绕前");
        System.out.println(jp.getSignature());
        // 执行方法
        Object proceed = jp.proceed();
        System.out.println(proceed);

        System.out.println("环绕后");
    }
}

配置文件配置

<!--方式三:注解-->
<!--引入自定义切面类-->
<bean id="annotationAspect" class="com.demo.aspect.AnnotationAspect"/>
<!--开启注解支持-->
<aop:aspectj-autoproxy/>

测试

整合Mybatis

Maven引入

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <!--MyBatis相关-->
    <dependency><!--数据库驱动-->
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.22</version>
    </dependency>
    <dependency><!--mybatis-->
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.6</version>
    </dependency>
    <dependency><!--mybatis-spring整合-->
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>2.0.6</version>
    </dependency>
    <!--MyBatis相关-->
    <!--spring相关-->
    <dependency><!--Spring核心-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.3.3</version>
    </dependency>
    <dependency><!--事务-->
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.3.3</version>
    </dependency>
    <dependency><!--AOP-->
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.6</version>
    </dependency>
    <!--spring相关-->
</dependencies>

<build>
    <!-- 防止资源导出错误 -->
    <resources>
        <resource>
            <directory>src/main/resources</directory>
            <includes>
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <filtering>true</filtering>
        </resource>
    </resources>
</build>

mybatis-config.xml

<?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>

    <settings>
        <!--开启日志-->
        <setting name="logImpl" value="STDOUT_LOGGING"/>
        <!--开启下划线转驼峰-->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
        <!--开启二级缓存-->
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <!--别名配置-->
    <typeAliases>
        <package name="com.demo.entity"/>
    </typeAliases>
    
    <!--Mapper注册-->
    <mappers>
        <package name="com.demo.mapper"/>
    </mappers>
</configuration>

appcationContext.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: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/context
       https://www.springframework.org/schema/context/spring-context.xsd">

    <!--加载Properties配置文件-->
    <context:property-placeholder location="classpath:db.properties"/>

    <!--DataSource-->
    <!--使用Spring的数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </bean>

    <!--SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <!--绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
    </bean>

    <!--以下两个配置需要再增加一个实现类-->
    <!--sqlSession-->
    <!--<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">-->
    <!--    <constructor-arg ref="sqlSessionFactory"/>-->
    <!--</bean>-->

    <!--注册userMapperImpl-->
    <!--<bean id="userMapper" class="com.demo.mapper.UserMapperImpl">-->
    <!--    <property name="sqlSession" ref="sqlSession"/>-->
    <!--</bean>-->

    <!--不需要增加实现类-->
    <!--MapperFactoryBean 将会负责 SqlSession 的创建和关闭-->
    <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
        <property name="mapperInterface" value="com.demo.mapper.UserMapper"/>
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

</beans>

*新增加的实现类:

public class UserMapperImpl implements UserMapper{
    private SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    /**
     * 获取全部用户
     */
    @Override
    public List<User> getUserList() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.getUserList();
    }
}

测试

@Test
public void testGetUserList() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
    List<User> userList = userMapper.getUserList();
    for (User user : userList) {
        System.out.println(user);
    }
}

声明式事务

新增事务约束

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

http://www.springframework.org/schema/tx 
http://www.springframework.org/schema/tx/spring-tx.xsd">

配置事务管理器

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

配置事务环绕

<!--配置事务环绕-->
<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="search*" propagation="REQUIRED"/>
        <tx:method name="get" read-only="true"/>
        <tx:method name="*" propagation="REQUIRED"/>
    </tx:attributes>
</tx:advice>

配置aop

<!--配置aop织入事务-->
<aop:config>
    <aop:pointcut id="txPointcut" expression="execution(* com.demo.service.*.*(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
</aop:config>

测试

public class UserServiceImpl {
    private UserMapper userMapper;

    public void setUserMapper(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    public void test() {
        userMapper.addUser(new User(10, "root10", "root10"));
        int i = 10/0;
        userMapper.deleteUser(10);
    }
}
<bean id="userService" class="com.demo.service.UserServiceImpl">
    <property name="userMapper" ref="userMapper"/>
</bean>
@Test
public void testTransaction() {
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserServiceImpl userService = context.getBean("userService", UserServiceImpl.class);

    userService.test();
}

结果:出错事务回滚,没有插入进去

注解式事务

posted @ 2021-01-15 21:06  神乐g  阅读(83)  评论(0编辑  收藏  举报