Spring

spring 源码 

一. IOC 加载

IOC:控制反转    以前我们创建对象需要new,现在交给IOC做,用的时候,我们自己去取

IOC容器,用来存放bean,按照map (ConcurrentHashMap) 结构存放bean

IOC容器工作流程

1. 撰写配置文件(可以是xml,yaml,json,properties)

2. 抽象读取配置文件接口,定义配置文件的规范(BeanDefinitionReader)

3. 读取bean的定义信息(BeanDefinition)

4. 通过反射取实例化bean对象

5. 初始化bean对象

6. 得到完整的bean对象

1. Environment 读取环境变量,加载系统信息

2. 修改bean定义信息中的变量(${aaa})

将其替换为 真实的 内容

例如:<bean id="druid" class="com.alibaba.druid.xxx">

                <propertis name="username" value="${jdbc.username}"/>

             </bean>

那么在spring实例化对象 druid 之前,参数username就必须在实例化之前替换就位。

那么这个就位的过程,就应该在 BeanDefinition 获取 Bean的配置信息的时候,

针对不同的对象(例如druid),去写一个实现类,实现 BeanFactoryPostProcessor ,

从而重写 方法,从而替换配置文件中的 ${xx} 的内容。

3. 实例化对象步骤

建立在 IOC container 之上的 BeanFactory  ,实例化对象

1. 获取class         

        (1) Class  clazz = Class.forName("完全限定名");

        (2) Class clazz= 对象.getClass();

        (3) Class clazz = 类.class;

2. 反射获取对象 

        Constructor ctor = clazz.getDeclareConstructor();

        Object obj = ctor.newInstance();

4. 初始化bean

5. spring 源码的加载

AOP:面向切面,遵循 ocp原则  对扩展开放,修改关闭

AOP 动态代理的方法有 cglib、jdk

Application.context

依赖注入

spring 使用

Spring 是一个工厂,专门负责生成Bean (对象)

提供AOP编程,可以方便的对程序进行权限拦截,运行监控等

只需要通过配置就可以完成对事务的管理,无需手动编程

通过注解对程序进行测试

不排斥其他框架

降低了JAVAEE的使用难度,内部集成了JDBC、JavaMail、远程调用等

     如果要,要引入 aopalliance(aop联盟)、spring-aop (spring整合aop)、aspectjweaver、spring-aspects        context       jar包 

 简单了解spring

#UserImpl.class

public class UserImpl implements IUser {
    @Override
    public void add() {
        System.out.println("添加用户");
    }
}

#IUser.interface

public interface IUser {

    public void add();

}

#bean.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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
    http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/tx
    http://www.springframework.org/schema/tx/spring-tx.xsd
    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 
    http://www.springframework.org/schema/mvc 
    http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--配置一个bean对象-->
    <bean id = "userBean" class="com.zx.service.impl.UserImpl"></bean>

</beans>

#Lesson01.class

public class Lesson01 {

    @Test
    public void test(){
//      加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//        从spring容器中获取Bean对象
        IUser user = (IUser)context.getBean("userBean");

        user .add();

    }

}

依赖注入

要求:属性要生成get、set方法

<bean id = "userBean" class="com.zx.service.impl.UserImpl">
        <!--依赖注入-->
        <property name="name" value="zx" ></property>

 </bean>

装配Bean的三种方法

1.通过new实现类

#Lesson01

/*
        * 1.通过new实现类
        * */
//      加载配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
//        从spring容器中获取Bean对象
        IUser user = (IUser)context.getBean("userBean");
        user .add();

#IUser.interface

package com.zx.service;

public interface IUser {

    public void add();

}

#UserImpl.class

package com.zx.service.impl;

import com.zx.service.IUser;

public class UserImpl implements IUser {

    private String name  ;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public void add() {
        System.out.println("添加用户+"+name);
    }
}

#Bean.xml 

<!--配置一个bean对象-->
    <bean id = "userBean" class="com.zx.service.impl.UserImpl">
        <!--依赖注入-->
        <property name="name" value="zx" ></property>
    </bean>

2.通过静态工厂

#Lesson01.class

/*
        * 2.通过静态工厂
        * */
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");

        IUser user = (IUser)context.getBean("staticFactoryUserBean");

        user.add();

#createStaticFactory.class 

public class createStatFactory {

    public static IUser createBean(){
        return new UserImpl();
    }
}

#IUser.interface  、UserImpl.class  同上

#bean.xml

<!--静态工厂-->
    <bean id = "staticFactoryUserBean" class="com.zx.service.createStatFactory" factory-method="createBean"></bean>

3.实例工厂

#Lesson01.class

        /*
        * 3.实例工厂
        * */
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        IUser user = (IUser)context.getBean("facthroyUserBean");
        user.add();

#createFactory.java


public class createFactory {
    public IUser createUser(){

        return new UserImpl();
    }
}

#IUser.interface  、 UserImpl.class   同上

#bean.xml


    <!--实例工厂-->
    <bean id = "factoryUser" class ="com.zx.service.createFactory" ></bean>
    <bean id = "facthroyUserBean" factory-bean="factoryUser" factory-method="createUser"> </bean>

4.指定bean加载时的初始化方法与销毁方法

 <bean id = "factoryUser" class ="com.zx.service.createFactory" init-method="init" destory-method="destory" ></bean>

5.bean标签 scope取值singleton(默认)单例,prototype多例。

6.构造方法注入:<bean xx> <constructor-arg name="xx"  value="xx" /><beans/>

7.set方法注入,类需要生成set方法 <bean xx> <property name="xx" value=xx"/> </bean>

8.集合注入,

set / list / array方法

    <bean xx>      

         <property name ="xx" >

              <set / list / array>

                    <value></value>

              </set / list / array>

          </property >

    </bean>

map方法

  <bean xx>      

         <property name ="xx" >

             <map>

                   <entry key="xx" value=" xx"/>

              </map>

          </property >

    </bean>

IDEA  快捷键 Ctrl+Alt +T 试一试就知道

注解的使用

     如果要使用注解,要引入 spring-aop、context、spring-aspects 、aspectjweaver、spring-aop、aopalliance(aop联盟)  jar包

context   注解 

aspectj  通知

aop 面向切面的编程 、事务管理,横向抽取机制(动态代理)(注解必须要的 jar 包)

tx  事务通知

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

    <!--开启注解-->
    <context:annotation-config />
    <!--配置扫描注解的位置-->
    <context:component-scan base-package="com.zx"></context:component-scan>

@RestController   与Controller 差不多,返回的字符串不通过试图解析器,直接显示到页面

@Component   泛指组件  不好归类时用Component(一般会在最后实例化) 用法:@Component、@Component   ("beanName“)

@Qualifier       根据指定的id注入属性 

@Value  设置属性的值,放在属性的上方   用法:@Value、@Value("123")

@PostConstruct   指定Bean加载时初始化调用方法

@PreDestory   指定Bean销毁时调用方法

@Scope 指定单例或者多例   用法:@Scope||@Scope("singleton")  单例         @Scope("prototype") 多例

通知 需要jar包:aopalliance(aop联盟)、spring-aop (spring整合aop)、aspectjweaver、spring-aspects

    <!--配置aop-aspectj拦截注解生效-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

通知  (aspectj .jar )

@Aspect   声明切面类

@Before 前置通知  用法:@Before(value="execution(* com.zx.dao..*(..))")

@AfterReturning 后置通知 用法:@AfterReturning(value="execution(* com.zx.dao..*(..))",returning="方法返回接收参数名")

@Around  环绕通知,开始执行之前,和执行之后所执行的方法  用法:@Around(value="execution(* com.zx.dao..*(..))")

@AfterThrowing 异常抛出通知  用法:同上或者同上上

@After 最终final通知,无论是否存在异常,都会执行,相当与finall代码块的作用  用法:同上上

通知的xml配置

Before 可以在前置通知方法中加上形参JoinPoint 获得调用方法信息,例如:

@Before("execution(* com.zx.dao..*(..))")
public void beforMethod(JoinPoint joinPoint){
    System.out.println(joinPoint);
}
输出:execution(void com.zx.dao.UserDao.selectUser())
作用:可以知道此时是那个方法调用了通知

 AfterReturning 可以在后置通知方法中加上形参Object  获得方法返回值(可不获取返回值),例如:

@AfterReturning(value="execution(* com.zx.dao..*(..))",returning="returnValue")
public void afterMethod(Object returnValue){
    System.out.println(returnValue);
}
即returnValue的值为,所调用方法的返回值

Around   环绕通知,简单的选择是否执行目标方法

@Around(value="execution(* com.zx.dao..*(..))")
public Object aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable{
    System.out.println("前置通知");
    Object obj = joinPoint.proceed();  //执行目标方法,如果不执行该句,目标方法不会被执行
    System.out.println("后置通知");
    reutrn obj;
}
最后要将返回值返回回去,才算执行完方法
即returnValue的值为,所调用方法的返回值

web mvc 注解的使用    注:注解中的value属性值,是上一层的本层对象变量名

@Controller    web  层注解   用法:@Controller、@Controller("beanName")

@Service        Service层注解   用法:@Service、@Service("beanName")

@Repository   Dao层注解        用法:@Repository、@Repository("beanName")

@Autowired    注入参数,默认按照参数类型注入,如果存在两个相同Bean类型相同,则按照名称注入  用法:@Autowired

@Qualifier  一般和@Autowired配合使用,用户指定注入Bean的名称    用法:@Qualifier("beanName")

@Resource 作用等于@Autowired+@Qualifier  用法:@Resource(name="beanName")

!--配置扫描注解的位置-->
<context:component-scan base-package="com.zx"></context:component-scan>



<!--开启aop注解-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

AOP面向切面的编程   预编译方式各运行期间动态代理实现程序的统一维护的一种技术

OOP面向对象的编程

纵向-----继承   耦合度高

横向-----AOP

Listener监听器,监听ServletContext启动时,在web.xml中加载applicationContext.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">



  <!--配置spring的配置文件加载路径-->
  <!--默认是找/WEB-INF/applicationContext.xml 路径下的文件-->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>
  <!--监听器-->
  <!--加载ApplicationContext    防止重复加载,出现多个对象-->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>


</web-app>

配置定时任务

添加注解

<task:annotation-driven/>  
如果不能识别,则需要在配置文件中添加xsi

    @Component  
    public class TaskUtil{  
    @Scheduled(cron = "0 0/1 * * * ?")  
    public void task() {  
        String dateStr = BaseDateUtil.getFormatString(new Date(), "yyyy-MM-dd HH:mm:ss");  
        System.out.println("每分钟执行一次:" + dateStr);  
    }  
}  
 

1.顺序
              *     *      *         *     *      *    *     
格式: [秒] [分] [小时] [日] [月] [周] [年] 
2.序号 
  是否必填  允许填写的值 允许的通配符 
1  秒  是  0-59    , - * / 
2  分  是  0-59   , - * / 
3 小时  是  0-23   , - * / 
4  日  是  1-31   , - * ? / L W 
5  月  是  1-12 or JAN-DEC   , - * / 
6  周  是  1-7 or SUN-SAT   , - * ? / L # 
7  年  否  empty 或 1970-2099  , - * / 
3.通配符
* 表示所有值. 例如:在分的字段上设置 "*",表示每一分钟都会触发。 
? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。例如:要在每月的10号触发一个操作,但不关心是周几,所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ? 
- 表示区间。例如 在小时上设置 "10-12",表示 10,11,12点都会触发。 
, 表示指定多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发 
/ 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。在月字段上设置'1/3'所示每月1号开始,每隔三天触发一次。 
L 表示最后的意思。在日字段设置上,表示当月的最后一天(依据当前月份,如果是二月还会依据是否是润年[leap]), 在周字段上表示星期六,相当于"7"或"SAT"。如果在"L"前加上数字,则表示该数据的最后一个。例如在周字段上设置"6L"这样的格式,则表示“本月最后一个星期五" 
W 表示离指定日期的最近那个工作日(周一至周五). 例如在日字段上设置"15W",表示离每月15号最近的那个工作日触发。如果15号正好是周六,则找最近的周五(14号)触发, 如果15号是周未,则找最近的下周一(16号)触发.如果15号正好在工作日(周一至周五),则就在该天触发。如果指定格式为 "1W",它则表示每月1号往后最近的工作日触发。如果1号正是周六,则将在3号下周一触发。(注,"W"前只能设置具体的数字,不允许区间"-"). 
小提示 
'L'和 'W'可以一组合使用。如果在日字段上设置"LW",则表示在本月的最后一个工作日触发(一般指发工资 ) 
# 序号(表示每月的第几个周几),例如在周字段上设置"6#3"表示在每月的第三个周六.注意如果指定"#5",正好第五周没有周六,则不会触发该配置(用在母亲节和父亲节再合适不过了) 

4.常用示例: 
*/30 * * * * ?   每30秒
0 0/1 15,* * * ?  每分钟0/30 * * * * ? 每30触发一次
0 0 12 * * ? 每天12点触发 
0 15 10 ? * * 每天10点15分触发 
0 15 10 * * ? 每天10点15分触发 
0 15 10 * * ? * 每天10点15分触发 
0 15 10 * * ? 2005 2005年每天10点15分触发 
0 * 14 * * ? 每天下午的 2点到2点59分每分触发 
0 0/5 14 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发) 
0 0/5 14,18 * * ? 每天下午的 2点到2点59分(整点开始,每隔5分触发) 每天下午的 18点到18点59分(整点开始,每隔5分触发) 
0 0-5 14 * * ? 每天下午的 2点到2点05分每分触发 
0 10,44 14 ? 3 WED 3月分每周三下午的 2点10分和2点44分触发 (特殊情况,在一个时间设置里,执行两次或两次以上的情况) 
0 59 2 ? * FRI    每周5凌晨2点59分触发; 
0 15 10 ? * MON-FRI 从周一到周五每天上午的10点15分触发 
0 15 10 15 * ? 每月15号上午10点15分触发 
0 15 10 L * ? 每月最后一天的10点15分触发 
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发 
0 15 10 ? * 6L 2002-2005 从2002年到2005年每月最后一周的星期五的10点15分触发 
0 15 10 ? * 6#3 每月的第三周的星期五开始触发 
0 0 12 1/5 * ? 每月的第一个中午开始每隔5天触发一次 
0 11 11 11 11 ? 每年的11月11号 11点11分触发(光棍节)

自定义注解

@Target  表明该注解可以应用的java元素类型

Target类型描述
ElementType.TYPE应用于类、接口(包括注解类型)、枚举
ElementType.FIELD应用于属性(包括枚举中的常量)
ElementType.METHOD应用于方法
ElementType.PARAMETER应用于方法的形参
ElementType.CONSTRUCTOR应用于构造函数
ElementType.LOCAL_VARIABLE应用于局部变量
ElementType.ANNOTATION_TYPE应用于注解类型
ElementType.PACKAGE应用于包
ElementType.TYPE_PARAMETER1.8版本新增,应用于类型变量)
ElementType.TYPE_USE1.8版本新增,应用于任何使用类型的语句中(例如声明语句、泛型和强制转换语句中的类型)

@Retention  表明该注解的生命周期

生命周期类型描述
RetentionPolicy.SOURCE编译时被丢弃,不包含在类文件中
RetentionPolicy.CLASSJVM加载时被丢弃,包含在类文件中,默认值
RetentionPolicy.RUNTIME由JVM 加载,包含在类文件中,在运行时可以被获取到

   
@Document  表明该注解标记的元素可以被Javadoc 或类似的工具文档化
@Inherited  表明使用了@Inherited注解的注解,所标记的类的子类也会拥有这个注解

@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SoutAnnotation {
    String value() default "default value";
}

 1.使用反射读取

public class ReflexAnnotation {
    public static void main(String[] args) {
        ReflexAnnotation a = new ReflexAnnotation();
        a.test("strTest");
    }
    @SoutAnnotation("testSout")
    public void test(String str){
        try{
            Class stuClass = this.getClass();
            Method stuMethod = stuClass.getMethod("test",String.class);
            if(stuMethod.isAnnotationPresent(SoutAnnotation.class)){
                System.out.println("ReflexAnnotation类上配置了SoutAnnotation注解!");
                //获取该元素上指定类型的注解
                SoutAnnotation cherryAnnotation = stuMethod.getAnnotation(SoutAnnotation.class);
                System.out.println("name: " + cherryAnnotation.value());
            }else{
                System.out.println("Student类上没有配置SoutAnnotation注解!");
            }
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}

2. 用注解方式加aspect切面 配置操作动作 (推荐)

@Target(value = {ElementType.METHOD})
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface SoutAnnotation {
    String value() default "default value";
}
@Component
@Aspect
public class SoutAspect {

    @Around(value="@annotation(com.tencent.gov.egb.app.test.annotation.SoutAnnotation)")
    public Object soutAround(ProceedingJoinPoint joinPoint) throws Throwable {
        //执行目标方法,如果不执行该句,目标方法不会被执行
        Object obj = joinPoint.proceed();
        System.out.println("后置通知");
        return obj;
    }
}
@RestController
public class SoutController {
    @GetMapping("/sout")
    @SoutAnnotation
    public String sout(@RequestParam("a")String a ){
        System.out.println("sout");
        return "sout";
    }
}

那么如果方法添加了 @SoutAnnotation 注解,就会被切面类拦截,并执行切面方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值