spring5

Author Avatar
kevin
发表:2020-04-08 20:00:00
修改:2024-10-13 20:34:32

spring5常用配置

需要写一个配置文件

<?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 class="com.stu.pojo.Collection" id="collection">
</beans>
  • bean标签
  • 通过配置bean标签告诉IOC容器需要创建对象的组件是什么
    • id属性:唯一标识,指定Bean的名称,在Bean被别的类依赖时使用
    • name属性:用于指定Bean的别名,如果没有id,也可以用name
      • 少用,是为了以前的框架准备的
    • class属性:用于指定Bean的来源,要创建Bean的class类,需要全限定名
    • scpoe:属性
      • singleton:默认值,单例,加载配置文件的时候调用
      • prototype:原型的意思,在这里是多例,比如SqlSession这个特殊的对象
        • 调用getbean方法的时候调用
  • 默认使用无参构造方法反射创建对象
    • 否则抛出异常

IOC原理

为了解耦,把对象创建和对象调用交给spring

原理

  • xml解析出全类名
  • 工厂模式
  • 通过反射创建实例

IOC接口

  • IOC容器本质就是对象工厂
    • IOC对象容器的本质,map容器,concurrentHashMap集合
  • 有两种实现方式,两个重要的接口
    • BeanFactory
      • spring内部使用接口,不提供开发人员进行使用,
      • 抉择配置文件不会创建对象,获取对象时才去创建
    • ApplicationContext
      • BeanFactory的子接口,给开发人员使用
      • 加载配置文件时,就会创建对象,配置文件里的所有对象
      • 接口实现类两个
        • FileSystemXmlapplicationContext
          • 文件的路径
        • ClassPathXmlapplicationContext
          • 加载配置文件

导入Spring框架依赖jar包

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>

获得spring容器创建的对象

通过类的class文件对象获取
 //读取配置文件
    private  ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

  Collection bean = context.getBean(Collection.class);
        xxx = bean.getxxx();

依赖注入-DI

dependency Inject(依赖注入的意思)

直白点说,为类的字段赋值就是注入 ----------------由自己赋值,变成由spring赋值

值:来自对象容器的值

准确的说,注入的是set方法名,去掉set,方法小写开头,

字段名随便,类似,vue调用get方法,无需字段也可-

set方法注入

value属性注入的是一个值

    <bean class="com.stu.pojo.Collection" id="collection">
<property name="userName" value="叶子"></property>
        </bean>

注入自定义对象

ref表示引用,refrence 引用另一个 对象的ID

  <!-- ref 属性:通过 bean 的 id 引用另一个 bean -->
    <property name="happyMachine" ref="happyMachine"/>

构造方法注入--有参构造,了解即可

  • spring值用双引号,会自动转换
  • name属性是有参构造参数名,不关字段名的事
<bean class="com.stu.pojo.Collection" id="collection">
<constructor-arg name="id" value="99"></constructor-arg>
    <constructor-arg name="money" value="8.55"></constructor-arg>
    <constructor-arg name="name" value="李四"></constructor-arg>
           </bean>

复杂数据类型注入

<!--        复杂数据注入-->

<!--        数组注入-->
        <property name="myStrs">
            <array>
                <value>数组1</value>
                <value>数组2</value>
                <value>数组3</value>
            </array>
        </property>

<!--        list注入-->
        <property name="myList">
            <list>
                <value>列表1</value>
                <value>列表2</value>
                <value>列表3</value>
            </list>
        </property>

<!--        输出map-->
        <property name="myMap">
            <map>
                <entry key="key1" value="value1"></entry>
                <entry key="key2" value="value2"></entry>
                <entry key="key3" value="value3"></entry>
            </map>
        </property>

<!--        输出配置文件-->
        <property name="myPro">
            <props>
                <prop key="pro1">value1</prop>
                <prop key="pro2">value2</prop>
                <prop key="pro3">value3</prop>
            </props>
        </property>

p命名空间方式注入(了解)

引入p命名空间的约束

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

<!--注入简单类型数据-->
<bean class="com.stu.pojo.Collection" id="collection" p:userName="叶子">
</bean>

直接写在bean里

引入外部属性文件用于给Bean注入属性

<!-- MySQL驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.3</version>
</dependency>
<!-- 数据源 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.0.31</version>
</dependency>

创建外部属性文件

jdbc.user=root
jdbc.password=root
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.driver=com.mysql.jdbc.Driver

引入命名空间

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

引用外部配置文件

<!-- 引入外部属性文件 -->
<context:property-placeholder location="jdbc.properties"></context:property-placeholder>

在spring的配置文件中使用引入的jdbc.properties文件中的数据

<!--[重要]给bean的属性赋值:引入外部属性文件 -->
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="url" value="${jdbc.url}"/>
    <property name="driverClassName" value="${jdbc.driver}"/>
    <property name="username" value="${jdbc.user}"/>
    <property name="password" value="${jdbc.password}"/>
</bean>

注解管理bean

注解的优势

注解本身并不能执行,注解本身仅仅只是做一个标记

注解开发比使用XML更加简洁明了

包扫描

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

要排除的组件

<!--        包扫描-->
    <context:component-scan base-package="com.stu"/>
 <!-- context:exclude-filter标签:指定排除规则 -->
    <!-- type属性:指定根据什么来进行排除,annotation取值表示根据注解来排除 -->
    <!-- expression属性:指定排除规则的表达式,对于注解来说指定全类名即可 -->
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

扫描指定组件

只扫描某种注解

<!--        包扫描-->
    <context:component-scan base-package="com.stu" use-default-filters="false"/>
  <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>

常用的注解

Component注解

该注解主要用在普通类上,比如Utils类

该注解衍生出了以下三个注解,用法完全一样,为了更加清晰的提现层的概念。

四个在本质上是没有区别的

//配置数据库相关
//引入外部文件
@Component
@PropertySource("jdbc.properties")
public class JdbcConfig {
}

Controller注解

主要用在控制层的类上

@Controller
public class UserController {
}

Service注解

业务层的实现类

@Service
public class UserServiceImpl implements UserService {
}

Repository注解

持久层的实现类上

将来由Mybatis负责扫描,也不需要使用Repository注解。

@Repository
public class UserDaoImpl implements UserDao {
}

注解属性value

单一值,可以注解在字段上

//配置数据库相关
//引入外部文件
@Component
@PropertySource("jdbc.properties")
public class JdbcConfig {

    @Value("${jdbc.user}")
    private String user;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.driver}")
    private String driver;
}

根据类型装配

Autowired注解----------常用 byType类型

按照类型注入,如果无法确定唯一类型(接口有多个实现类),

需要配合注解@Qualifier的使用,@Qualifier("id")。

无需setXXX方法。暴力反射注入

根据名字装配,一般和autowried一起使用

@Qualifier注解-------根据byName装配,适合多个实现类时

@Autowired
    @Qualifier("accountDao")
    private AccountDao accountDao;

纯注解开发

为了给将来学习SpringBoot打基础

SpringBoot中,就是完全舍弃XML配置文件

相关注解

  • @Configuration标识当前类是Spring的一个配置类
  • @ComponentScan替代xml中的<context:component-scan/>
  • @Import引入其他配置类,被引入的配置类可以不加@Configuration注解
  • @PropertySource:引入外部properties文件,注意加classpath:
  • @Value对成员变量赋值------除了对象以外的
  • @Bean将一个方法的返回值对象加入到Spring的容器当中管理
  • @Qualifier可以使用在方法上,表明对应的形参引入/注入的对象类型
    • 和autowired一起使用

创建配置类

配置类上要添加一个@Configuration注解

使用@ComponentScan注解进行包扫描

//注明是配置文件
//包扫描
@Configuration
@ComponentScan("com.stu")
public class SpringConfig {
}

在配置类中配置bean

使用@Bean注解配置第三方的类的IOC

从properties文件中读取数据

//配置数据库相关
//引入外部文件
@Component
@PropertySource("jdbc.properties")
public class JdbcConfig {

    @Value("${jdbc.user}")
    private String user;
    @Value("${jdbc.password}")
    private String password;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.driver}")
    private String driver;
}

配置连接池和工具类

//引入连接池
    @Bean          返回值到ioc容器
    public  DataSource getDataSource(){
        //获得连接池对象
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setUsername(user);
        druidDataSource.setPassword(password);
        druidDataSource.setUrl(url);
        druidDataSource.setDriverClassName(driver);
        return druidDataSource;
    }
    //注册qr数据库类,传入连接池参数
    @Bean
    public QueryRunner getQueryRunner(DataSource dataSource){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        return queryRunner;
    }

Spring整合junit4

junit版本必须要4.12及以上

加入依赖

<!--    junit整合-->
    <dependency
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>5.2.6.RELEASE</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>

junit运行的时候底层使用了Runner对象,有一个默认使用的Runner对象。

Spring对junit的支持,其实是自己实现了一个Runner对象

//Spring框架中的Runner对象,替换Junit中的runner对象
@RunWith(SpringJUnit4ClassRunner.class)
//框架启动入口,配置类启动
@ContextConfiguration(classes = SpringConfig.class)
public class TestUser {
    //注入业务层
    @Autowired
    private UserService userService;
//test整合
    @Test
    public void testNoXml(){
        User user = new User();
        user.setUserName("小龙");
        user.setSex("2");
        user.setBirthday(new Date());
        user.setAddress("广西");
        userService.saveUser(user);
    }

xml配置文件启动

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")

AOP面向切面编程

aop底层使用的是动态代理模式

未经过代理的情况:

调用方法1-2024-10-13-20-32-38.png

使用了代理模式的情况:

调用方法2-2024-10-13-20-32-39.png

基于注解的AOP用到的技术

spring基于注解的aop-2024-10-13-20-29-20.png
动态代理(InvocationHandler):JDK原生的实现方式,需要被代理的目标类必须实现接口。因为这个技术要求代理对象和目标对象实现同样的接口

cglib:通过继承被代理的目标类实现代理,所以不需要目标类实现接口。

AspectJ:本质上是静态代理,将代理逻辑“织入”被代理的目标类编译得到的字节码文件,所以最终效果是动态的。weaver就是织入器。Spring只是借用了AspectJ中的注解。

连接点

类的哪些方法可以被增强,这些方法就是连接点

切入点

实际被增强的方法,叫做切入点

而切入点指的则是方法中真正要去配置增强或者配置修改的地方

//相同切入点抽取,作为公共的
@Pointcut("execution(* com.stu.aopanno.User.add(..))")
    public void pointdemo(){ }

通知(增强)

实际增强的逻辑部分称为通知,

原本代码不存在的,自己后添加的

比如在登录,添加逻辑判断,这部分就是通知或者说是增强

  • 前置通知

    • 目标方法执行

    • @Before表示前置通知
       @Before("pointdemo()")
      
  • 后置通知(返回通知)

    • 目标方法返回值后通知(寿终正寝

    • 如果出现异常,下面代码不会执行
      方法返回值之后执行
       @AfterReturning("pointdemo()")
      
  • 环绕通知-------用的比较多

    • 通常情况下,环绕通知都是独立使用的,不要和上面的四种通知类型混合使用

    • 方法有一个参数

      • ProceedingJoinPoint进程切入点对象,执行我们的业务逻辑方法
      • proceed() 执行我们自己的业务逻辑方法
    • //环绕通知,之前之后都会通知
       @Around("pointdemo()")
        public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
              System.out.println("环绕之前");
              //执行被增强的方法
              proceedingJoinPoint.proceed();
              //如果出现异常,下面代码不会执行
              System.out.println("环绕之后");
          }
      
  • 异常通知

    • 目标方法异常结束后执行(死于非命

    • @AfterThrowing("pointdemo()")
      
  • 最终通知

    • 目标方法最终结束后执行

    • 不管有没有异常都会执行

    • //最终通知,不管有没有异常,都会执行
      @After("pointdemo()")

切面-是一个过程,一个动作

把通知添加到切入点过程就叫做切面

添加依赖

 <!-- spring-aspects会帮我们传递过来aspectjweaver -->
  <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aspects</artifactId>
          <version>5.2.6.RELEASE</version>
      </dependency>

切入点表达式

调用方法4-2024-10-13-20-32-39.png

用于描述将代理逻辑套用在哪些目标方法上

对哪个类里面的哪个方法进行增强

语法结构

固定单词

execution()

调用方法5-2024-10-13-20-32-39.png

三种常用表达式

  • execution(* com.stu.aopanno.User.add(..) )
    • 为了方便,权限修饰符一般设为*表示任意类型,后面有空格
    • 返回类型可以省略
    • 跟着类的全类名点上方法名
    • 方法名和全类名前面用点连接
      • 括号里面是参数列表,为了方便,一般用..表示
      • ....表示有无参数均可,参数可以是任意类型
  • execution(* com.stu.aopanno.User.*(..) )
    • 表示对类里面的所有方法进行增强
  • execution(* com.stu.aopanno.*.*(..) )
    • 表示这个包所有类,类里面的所有方法进行增强
  • execution(* com..service.*.*(..) )--------石老师推荐
    • com下的任何子包里的service

基于注解的AOP

置Spring框架启动类

//设置为配置文件
@Configuration
//扫描包
@ComponentScan("com.stu")
//开启aspect生成代理对象
//启用动态代理
@EnableAspectJAutoProxy
public class SpringConfig {
}

原始类

//添加到ioc容器
@Component
//原始类
public class User {
    public void add(){
        System.out.println("我是添加用户的方法");
    }
}

增强类

//原始类增强
@Component
//生成代理对象
@Aspect
public class UserProxy {
    //相同切入点抽取,作为公共的
    @Pointcut("execution(* com.stu.aopanno.User.add(..))")
    public void pointdemo(){

    }
    // @Before表示前置通知
    //指定哪个类的哪个方法做一个增强
    @Before("pointdemo()")
    public void before(){
        System.out.println("前置通知方法");
    }
    //最终通知切入点表达式,方法之后执行
    //最终通知,不管有没有异常,都会执行
    @After("pointdemo()")
    public void after(){
        System.out.println("后置通知方法");
    }
    //后置通知,方法返回值之后执行,    //如果出现异常,下面代码不会执行
    @AfterReturning("pointdemo()")
    public void afterReturning(){
        System.out.println("@AfterReturning方法,貌似也是后置");
    }

    //异常通知,出现异常时才会通知
    @AfterThrowing("pointdemo()")
    public void afterThrowing(){
        System.out.println("@AfterThrowing异常通知");
    }

    //环绕通知,之前之后都会通知
    @Around("pointdemo()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("环绕之前");
        //执行被增强的方法
        proceedingJoinPoint.proceed();
        //如果出现异常,下面代码不会执行
        System.out.println("环绕之后");
    }
}

相同切入点抽取,作为公共的

在一处声明切入点表达式之后,其他有需要的地方引用这个切入点表达式

双点可以使用*代替

 @Pointcut("execution(* com.stu.aopanno.User.add(..))")
    public void pointdemo(){ }

同一个类内部引用切入点

通过方法名引入

 // @Before表示前置通知
    //指定哪个类的哪个方法做一个增强
    @Before("pointdemo()")
    public void before(){
        System.out.println("前置通知方法");
    }

在其它类中引用切入点

通过全限定名引入

   @Before("com.stu.aopanno.UserProxy.pointdemo()")
	public void before2(){
        System.out.println("前置通知方法");
    }

对项目中的所有切入点进行统一管理

作为存放切入点表达式的类,可以把整个项目中所有切入点表达式全部集中过来,便于统一管理:

public class UserPoPointCut {
    @Pointcut("execution(* com.stu.aopanno.User.add(..))")
    public void userPointCut(){
    }
}

JDBCTemplate

导入依赖

   <!-- Spring 持久化层支持jar包 -->
    <!-- Spring 在执行持久化层操作、与持久化层技术进行整合过程中,需要使用orm、jdbc、tx三个jar包 -->
    <!-- 导入 orm 包就可以通过 Maven 的依赖传递性把其他两个也导入 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>5.3.1</version>
    </dependency>

增删改操作

jdbcTemplate.update(String sql,Object... args)

查询操作

返回单个简单类型

jdbcTemplate.queryForObject(sql, 要返回的类class);

 //查询员工的个数
    String sql = "select count(emp_id) from t_emp";
    Long count = jdbcTemplate.queryForObject(sql, Long.class);

这两个是同样的方法

查询返回单个值(聚合函数)

jdbcTemplate.queryForObject(String sql,Class requiredType);

第二个参数,返回类型的类class

/查询账户总条数
 String sql = "select count(id) from account";
Integer count = jdbcTemplate.queryForObject(sql,Integer.class)

查询返回对象,有参的情况

jdbcTemplate.queryForObject(String sql,RowMapper rowMapper,Object...args)
rowmapper 是接口,返回不同类型数据,有实现类
RowMapper接口实现类BeanPropertyRowMapper,查询的结果集封装,适用单个对象或者集合

  //id查询账户
   String sql = "select id,name,money from account where id = ?";
 return jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Account>(Account.class),id);

查询//查询多个员工信息 返回list集合

jdbcTemplate.query(String sql,RowMapper rowMapper,Object...args)

RowMapper接口实现类BeanPropertyRowMapper,查询的结果集封装,适用单个对象或者集合

//查询多个员工信息,封装到List<Employee>中
  String sql = "select emp_id empId,emp_name empName, emp_salary empSalary from t_emp";
    List<Employee> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Employee.class));
评论