发表于: 2019-10-26 21:33:37

2 970


啥也不说就是干!!!

今天完成的事情:

1、DI(依赖注入)

什么是依赖:依赖是指 Bean 实例中的属性

属性分为:简单类型(8种基本类型和 String 类型)、POJO 类型、集合数组类型


什么是依赖注入

Dependency Injection依赖注入是 Spring 核心框架 IoC 的具体实现 

依赖注入的方式(基于XML)

1) 构造函数注入

<bean id="student" class="com.gerry.jnshu.bean.Student">
   <constructor-arg value="1" index="0"></constructor-arg>
   <!--如果字面值包含特殊字符可以使用![CDATA[]]包裹起来:比如 name 字面值为 <zhouzihui>-->
   <!--属性值也可以使用value子节点进行配置-->
   <constructor-arg>
       <value><![CDATA[<zhouzihui>]]></value>
   </constructor-arg>
  
</bean>

涉及的标签:constructor-arg

index:指定参数在构造函数参数列表的索引位置

name:指定参数在构造函数中的名称

value:它能赋的值是基本数据类型和 String 类型

ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean


2) set 方法注入


手动装配方式(XML 方式):通过bean标签的子标签property来完成,需要在类中指定setter方法。

a. 简单类型(value):

<bean id="student" class="com.gerry.jnshu.bean.Student">
  <property name="name" value="张三"></property>
   <property name="school" value="河南师范"></property>
   <property name="qq" value="12345678"></property>

</bean>

b. 引用类型(ref)

public class Student {
private int id;
   private Address address;

   public Address getAddress() {
      return address;
   }

    public void setAddress(Address address) {
     this.address = address;

   }

   //省略部分源码

}

对于 Student 中有个属性为引用类型 Address:

public class Address {
private String province;
   private String city;
   private String distinct;

   public String getProvince() {
      return province;
   }
   public void setProvince(String province) {
      this.province = province;

   }

   //省略部分源码

}

那么如果Student 想要注入 Address 属性,则需要使用 <ref> 标签,指向 Address bean

<bean id="student" class="com.gerry.jnshu.bean.Student">
  <property name="name" value="张三"></property>
   <property name="school" value="河南师范"></property>
   <property name="qq" value="12345678"></property>
   <property name="address" ref="address"></property>
</bean>

<bean id="address" class="com.gerry.jnshu.bean.Address">
   <property name="province" value="河南"></property>
</bean>

c. 集合类型

如果是数组或者 List 集合,注入配置文件的方式是一样的:

<bean id="collectionBean" class="com.gerry.jnshu.CollectionBean">
   <property name="arrs">
       <list>
           //如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签
           <value>美美</value>
           <value>小风</value>
           <bean></bean>
       </list>
   </property>
</bean>

 如果是Set集合,注入的配置文件方式如下:

<property name="sets">
   <set>
       //如果集合内是简单类型,使用value子标签,如果是POJO类型,则使用bean标签
       <value>哈哈</value>
       <value>呵呵</value>
   </set>
</property>

如果是Map集合,注入的配置方式如下:

<property name="map">
   <map>
       <entry key="老王2" value="38"/>
       <entry key="凤姐" value="38"/>
       <entry key="如花" value="29"/>
   </map>
</property>

如果是Properties集合的方式,注入的配置如下:

<property name="pro">
   <props>
       <prop key="uname">root</prop>
       <prop key="pass">123</prop>
   </props>
</property>

 

Spring 基于注解和 XML 混合方式使用:

学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。

关于实际的开发中到底使用xml 还是注解,不同的场景有着不同的使用习惯。所以这两种配置方式我们都需要掌握。

这里要把 spring 的 xml 配置内容改为使用注解逐步实现。


IoC 注解使用方法

1)spring配置文件中,配置context:component-scan标签

<!--开启注解扫描指定包中带有注解的类-->
<context:component-scan base-package="com.gerry.jnshu"/>

2)类上面加上注解 @Component 或者它的衍生注解 @Controller @Service @Repository

 IoC 注解(创建对象)相当于 <bean id="" class="">

1)  @Component注解

作用:

       把资源让 spring 来管理。相当于在 xml 中配置一个 bean。

属性:

       value:指定 bean 的 id。

       如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。

2)@Controller、@Service、@Repository注解

这三个注解都是针对@Component的衍生注解

他们的作用及属性都是一模一样的。他们只不过是提供了更加明确的语义化。

       @Controller:一般用于表现层的注解。

       @Service:一般用于业务层的注解。

       @Repository:一般用于持久层的注解。

细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写。


DI注解(依赖注入)相当于:<property name="" ref="">

1)@Autowired

@Autowired默认按类型装配(byType)

@Autowired是由AutowiredAnnotationBeanPostProcessor类实现

@Autowired是spring自带的注解

@Autowired默认情况下要求依赖对象必须存在,如果需要允许 null 值,可以设置它的 required 属性为 false,如:@Autowired(required=false)

如果我们想按名称装配(byName)可以结合 @Qualifier 注解进行使用


2) @Qualifier

在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。

它在给字段注入时不能独立使用,必须和@Autowire 一起使用;

但是给方法参数注入时,可以独立使用。


3)@Resource

@Resource默认按名称装配(byName),可以通过@Resource的name属性指定名称

@Resource属于J2EE JSR250规范的实现

@Resource如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,当找不到与名称匹配的bean时才按照类型进行装配。

但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

 推荐使用@Resource注解,因为这个注解是属于J2EE的,减少了与spring的耦合。这样代码看起就比较优雅。


关于注解和 XML 的选择问题

注解的优势:配置简单,维护方便(我们找到类,就相当于找到了对应的配置)

XML 的优势:修改时,不用改源码,不涉及重新编译和部署

基于注解的 IoC 配置已经完成,到此发现依然离不开 spring 的 XML 配置文件,那么能不能不写 application.xml 所有配置都用注解来实现呢?


Spring 基于纯注解的使用:

当我们需要摆脱 xml 配置文件时候,思考一下:

注解扫描配置(怎么去掉)

<!--开启注解扫描指定包中带有注解的类-->
<context:component-scan base-package="com.gerry.jnshu"/>

非自定义 Bean 配置(如:SqlSessionFactory 和 BasicDataSource 配置)

<!-- 配置sqlSessionFactory,SqlSessionFactoryBean是用来产生sqlSessionFactory的 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
   <!-- 加载数据源,使用上面配置好的数据源 -->
   <property name="dataSource" ref="dataSource"/>
</bean>

之前的创建方式为通过读取 XML 文件进行创建,去掉 XML 后,该如何创建 ApplicationContext 呢?

ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");

@Configuration

从 Spring3.0 开始可以使用 @Configuration 定义配置类,代替 xml 配置文件

相当于 xml 文件中的 <beans> 根标签

配置类内部包含一个或多个 @Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContext(AnnotationConfigWebApplicationContext)类进行扫描

并用于构建 bean 定义对象,初始化 Spring 容器

比如:

@Configuration
public class SpringConfiguration{
   public SpringConfiguration(){
      System.out.println("容器启动初始化。。。");
   }

}

@Bean

@Bean 标注在方法上(返回讴歌实例的方法),等价于 Spring 配置文件中的 <bean>

用来注册 bean 对象(主要用来配置非自定义的 bean,如 DruidDataSource、SqlSessionFactory)

包含的属性有:

1)name:给当前 @Bean 注解方法创建的对象指定一个名称(即 bean 的 id)如果不指定,默认与标注的方法名相同

2)@Bean 注解默认作用域为单例 singleton 作用域,可通过 @Scope("prototype")指定为原型作用域

比如:

@Configuration
public class SpringConfiguration{
   public SpringConfiguration(){
      System.out.println("容器启动初始化。。。");
   }
   @Bean
   @Scope("prototype")
   public StudentService studentService() {
      return new StudentServiceImpl();
   }
}

@ComponentScan

相当于 context:component-scan 标签

组件扫描器,扫描 @Component @Controller、@Service、@Repository 注解的类,该注解是编写在类上面,一般配合 @Configuration 注解一起使用

包含的属性有:

1)basePackages:用于指定要扫描的包

2)value:和 basePackages 作用一样

比如:

@Service
public class StudentServiceImpl implements StudentService{
   @Overide
   public void saveStudent(){
      System.out.println("保存学生 Service 实现");
   }
}

@Configuration
public class SpringConfiguration{
   public SpringConfiguration(){
      System.out.println("容器启动初始化。。。");
   }
   @Bean
   @Scope("prototype")
   public StudentService studentService(){
      return new StudentServiceImpl();
   }
}

@PropertySource

相当于 context:property-placeholder 标签 加载 Properties 配置文件,编写在类上面

包含的属性有:

1)value[] 用于指定 properties 文件路径,如果在类路径下,需要写上 classpath 

实例代码:

@Configuratio

@PropertySource("classpath:jdbc.properties")

public class JdbcConfig {
      @Value("${jdbc.driver}")
      private String driver;
      @Value("${jdbc.url}")
      private String url;
      @Value("${jdbc.username}")
      private String username;
      @Value("${jdbc.password}")
      private String password;
        /**
           * 创建一个数据源,并存入 spring 容器中
           *
           * @return
        */
      @Bean(name = "dataSource")
      public DataSource createDataSource() {
        try {
              ComboPooledDataSource ds = new ComboPooledDataSource();
              ds.setDriverClass(driver);
              ds.setJdbcUrl(url);
              ds.setUser(username);
              ds.setPassword(password);
              return ds;
         }catch (Exception e) {
              throw new RuntimeException(e);
         }
      }
}

properties 文件:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=root

@Import

相当于 Spring 配置文件中 import 标签,用来组合多个配置类

包含的属性:

value:用来指定其他配置文件类

@Configuration
@ComponentScan(basePackages = "com.gerry.jnshu")
@Import({ JdbcConfig.class })
public class SpringConfiguration {
}

@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
}

纯注解方式创建上下文容器

ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
StudentService studentService = context.getBean(StudentService.class);
studentService.insertInfo();

2、Spring AOP 

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程

AOP采取横向抽取机制,补充了传统纵向继承体系(OOP)无法解决的重复性代码优化(性能监视、事务管理、安全检查、缓存),将业务逻辑和系统处理的代码(关闭连接、事务管理、操作日志记录)解耦

Spring 基于 AspectJ 的 AOP 使用

Spirng 将 AspectJ 收录到自身的框架中,并且底层依然采用的是动态代理的方式

1)添加 AspectJ 的 AOP 依赖

<!-- 基于AspectJ的aop依赖 -->
  <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-aspects</artifactId>
      <version>5.0.7.RELEASE</version>
  </dependency>
  <dependency>
      <groupId>aopalliance</groupId>
      <artifactId>aopalliance</artifactId>
      <version>1.0</version>
  </dependency>

2)编写目标类和目标方法

配置目标类,将目标类交给 Spring IoC 容器管理

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


a. 使用 XML 实现 Spring AOP

编写通知(增强类,一个普通的类)

public class MyAdvice {
public void log(){
System.out.println("记录日志...");
   }
}

配置通知,将通知类交给 Spring IoC 容器管理

<!--配置通知、增强-->
<bean name="myAdvice" class="com.gerry.jnshu.service.MyAdvice"/>

配置 AOP 切面

<!--AOP配置-->
<aop:config>
   <aop:aspect ref="myAdvice">
       <aop:pointcut id="pointcut" expression="execution(void com.gerry.jnshu.service.impl.StudentServiceImpl.insertInfo())"/>
       <aop:before method="log" pointcut-ref="pointcut"/>
   </aop:aspect>
</aop:config>


关于切入点表达式:excution([修饰符] 返回值类型 包名.类名.方法名(参数))

修饰符:可忽略

返回值类型:必须有,但可以使用 * 通配符

包名:多级包名用 . 分割,包名可以使用 * 代替,多级包名可以使用多个 * 代替,如果中间想要省略包名可以使用 ..

类名:可以使用 * 代替,也可以写成 *ServiceImpl

方法名:也可以使用 * 代替,也可以写成 add*

参数:可以使用 * 代替,如果多个参数,可以使用 .. 代替

Student student = new Student();
student.setName("zhangsan");
StudentService studentService = (StudentService) ctx.getBean("studentService");
studentService.insertInfo(student);

通知类型:前置通知、后置通知、最终通知、环绕通知、异常抛出通知

1)前置通知:目标对象方法之前执行

<aop:before method="before" pointcut-ref="pointcut"/>

用于方法开始前进行校验

2) 后置通知:目标对象方法之后执行

<aop:after-returning method="afterReturning" pointcut-ref="pointcut"/>

用于方法之后处理

3)返回通知

<aop:after method="before" pointcut-ref="pointcut"/>

4) 最终通知:目标对象方法之后执行,有没有异常都会执行

<aop:after-return method="afterReturn" pointcut-ref="pointcut"/>

用于释放资源等

5)环绕通知:目标对象方法之前和之后都会执行

<aop:around method="around" pointcut-ref="pointcut"/>

用于事务、统计代码执行时机

6)异常抛出通知:在抛出异常后通知

<aop:after-throwing method=" afterThrowing " pointcut- ref="pointcut"/>

包装异常

b.使用 XML +注解 实现 Spring AOP

编写切面类(该类中还指定了切入点)

@Component("myAdvice")
@Aspect
public class MyAdvice {

     @Before(value="execution(* *..*.*ServiceImpl.*(..))")
     public void log(){
         System.out.println("记录日志...");
     }
}

配置切面类,交给 Spring 容器

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

开启 AOP 自动代理

<aop:aspectj-autoproxy />

这里记录个知识点:point 还可以单独在切面类中定义出来

@Pointcut(value = "execution(* *..*.*ServiceImpl.*(..))")
public void log2(){
    System.out.println("统一配置切入点...");
}
@Before("MyAdvice.log2()")
public void beforeMethod(){
    System.out.println("开始方法");
}

c.基于纯注解 Spring AOP 配置方式:@EnableAspectJAutoProxy

接下来的计划:

今天就学到这里,接下来学习 Spring 项目整合部分

遇到的问题:

暂无

收获:

掌握了Spring IoC 基本知识,大致了解了 AOP 的使用


返回列表 返回列表
评论

    分享到