spring5-配外部文件-spEL-工厂bean-FactoryBean-注解配bean

spring配外部文件

我们先在Spring里配置一个数据源

1.导c3p0包,这里我们先学一下hibernate持久化框架,以后用mybites.

<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-core</artifactId>
  		<version>5.2.17.Final</version>
  	</dependency>
  	<dependency>
  		<groupId>org.hibernate</groupId>
  		<artifactId>hibernate-c3p0</artifactId>
  		<version>5.3.0.Final</version>
  	</dependency>

2.安装mysql 8.x数据库(参考相关资料),导入驱动包

<dependency>
  		<groupId>mysql</groupId>
  		<artifactId>mysql-connector-java</artifactId>
  		<version>8.0.16</version>
  	</dependency>

3.看具体代码:

    <!-- 配置c3p0,ComboPooledDataSource对象,它指向一个数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="user" value="root"></property>
       <property name="password" value="xiong"></property>
       <property name="jdbcUrl" value="jdbc:mysql://124.220.60.104:3306/spring5"></property>
       <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
    </bean>

测试数据库连接

	@Test
	public void dataSourcetest() throws SQLException{
		ApplicationContext ct = new ClassPathXmlApplicationContext("dataSource.xml");
		DataSource dataSource = (DataSource)ct.getBean("dataSource");
		System.out.println(dataSource.getConnection());
	}
-----------------------------------------------------------------
com.mchange.v2.c3p0.impl.NewProxyConnection@45c7e403 [wrapping: com.mysql.cj.jdbc.ConnectionImpl@2925bf5b]   //连接成功,产生一个代理数据库服务器

              上面这样配置,写死了数据是相对以后转生产环境中修改是十分不方便的,生产环境中,我们极有可能会把dao层打jar包,又分成多模块开发,所以修改起来是十分麻烦,所以我们会把值写成键值对的形式单独保存到一个文件中

使用外部文件保存数据值,再配spring配置文件

在这里插入图片描述
jdbc.properties

jdbc.user=root
jdbc.password=xiong
jdbc.url=jdbc:mysql://124.220.60.104:3306/spring5
jdbc.driverClass=com.mysql.cj.jdbc.Driver

dataSouce.xml–spring配置文件

    <!-- 配置c3p0,ComboPooledDataSource对象,它指向一个数据源 -->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
       <property name="user" value="${jdbc.user}"></property>
       <property name="password" value="${jdbc.password}"></property>
       <property name="jdbcUrl" value="${jdbc.url}"></property>
       <property name="driverClass" value="${jdbc.driverClass}"></property>
    </bean>

Spring的表达式语言spEL

       Spring表达式语言(SpEL):是一个支持运行时查询和操作对象图的强大表示是语言,是一种可以与一个基于spring的应用程序中的运行时对象交互的东西。总得来说SpEL表达式是一种简化开发的表达式,通过使用表达式来简化开发,减少一些逻辑、配置的编写。

       语法类似于 EL:SpEL 使用 #{…} 作为定界符 , 所有在大括号中的字符都将被认为是 SpEL , SpEL 为 bean 的属性进行动态赋值提供了便利。
通过 SpEL 可以实现:

通过 bean 的 id 对 bean 进行引用,用了SpEL在bean标签中可以用value代替ref。
可以像EL一样用点运算符调用方法以及对象中的属性。
计算表达式的值
正则表达式的匹配。

SpEL 字面量,意义不大,spring内部本身有数据类型的自动转换机制,直接写值就好了,不必用SqEL,了解:

整数:#{8}   实际上,直接写成“8”即可,如前面讲的:p:id="1",就表示1
小数:#{8.8}
科学计数法:#{1e4}
String:可以使用单引号或者双引号作为字符串的定界符号。
Boolean:#{true}

SpEL引用bean , 调用它属性和方法:

引用其他对象:#{car}
引用其他对象的属性:#{car.price}
调用其它方法 , 还可以链式操作:#{person.pet.toString()}
调用静态方法静态属性:#{T(java.lang.Math).PI}
Spring EL 操作List、Map集合取值

//SpEL支持的运算符号:

算术运算符:+,-,*,/,%,^(加号还可以用作字符串连接)
比较运算符:< , > , == , >= , <= , lt , gt , eg , le , ge
逻辑运算符:and , or , not , |
if-else 运算符(类似三目运算符):?:(temary), ?:(Elvis)
正则表达式:#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'}

例:引用对象举例
       为了测试,我在Pet类中,添加成员属性private double price及get/set方法,在Person类中添属性成员private double petPrice,private double pi及get/set方法,petPrice属性在spring创建对象时,引用Pet对象中定义的值,pi属性引用lang包中的静态成员常量。

   <!-- springEL,测试 -->
     <bean id="cat1" class="cn.ybzy.springdemo.model.Pet" p:id="1" p:type="cat" p:price="1000.0"></bean>
     
     <bean id="person" class="cn.ybzy.springdemo.model.Person">
        <property name="id" value="1"></property>
        <property name="pet" value="#{cat1}"></property>
        <property name="petPrice" value="#{cat1.price}"></property>
        <property name="pi" value="#{T(java.lang.Math).PI}"></property>
     </bean>
	
	/*
	 * spring EL 测试
	 */
	@Test
	public void springEltest() {
		ApplicationContext ct = new ClassPathXmlApplicationContext("springEL.xml");
		Person person=(Person)ct.getBean("person");
		System.out.println(person);
		System.out.println(person.getPi());
	}
--------------------------------------------------------------------
Person [id=1, pet=Pet [id=1, type=cat, price=1000.0], cars=null, petPrice=1000.0]
3.141592653589793	

例:springEL运算符演示,这里我只举,三目运算会和正则表达式匹配运算
       为了测试,我在Person类中添加test属性,private String test及get/set方法,它会根根价格来作两个判 断,大于800元,是土豪,小于100元是普通人养宠物。

 <property name="test" value="#{cat1.price > 800 ? '土豪' : '普通人'}"></property>
   System.out.println(person.getTest());
---------------------------------------------------------------------
土豪

正则表达式测试,字符串 ''aaaa98734"不全是数字,所以匹配0-9多个数字是错的

<property name="test" value="#{'aaa98734' matches '[0-9]+'}"></property>
  System.out.println(person.getTest());
---------------------------------------------------------------------
false

例:引用List,Map
在这里插入图片描述
map[‘1’]中的1是KEY
在这里插入图片描述

Spring通过工厂方法进行配置

       在Spring的世界中, 我们通常会利用 xml配置文件 或者 annotation注解方式来配置bean实例!
在第一种利用 xml配置文件 方式中, 还包括如下三小类

  1. 反射模式(我们前面的所有配置都是这种模式)
  2. 工厂方法模式
  3. Factory Bean模式

工厂方法进行配置
       静态工厂方法方式是非常适用于作为1个bean容器, 只不过bean集合定义在工厂类里面而不是项目xml配置文件里面。
       缺点也比较明显, 把数据写在class里面而不是配置文件中违反了我们程序猿的常识和spring的初衷。当然优点就是令人恶心的xml配置文件更加简洁。所以,工厂方法的配置,了解一下就行了,个人建议不要在项目中使用,但要了解,以后看到项目中有这样的方式,能看得懂。

FactoryBean 来配置Bean

       spring通过FactoryBean配置,比前面的工厂方法配置Bean要重要些,因为我们整合很多第三方的框架的时候,需要用到FactoryBean来配置第三方框架中的bean 对象,从而把第三方框架整合到spring中来!当然在整合这些第三方框架的时候,这个FactoryBean一般都是我们引入的jar包中,人家给写好了的,我们会用就行,但知道原理也是好的!
例:通过FactoryBean接口的实现类CarFactoryBean,spring中配Car的bean:id="bwn"是CarFactoryBean的bean名,再通过构造方法注入Car的bean,则试:输出Car对象的toString()中的内容

CarFactoryBean.java

public class CarFactoryBean implements FactoryBean<Car>{
	private String type;
	public CarFactoryBean(String type) {
		this.type=type;
	}
	
    //返回我们要配置的Bean 对象
	@Override
	public Car getObject() throws Exception {
		
		return new Car(101,type);
	}
    //返回我们配置的Bean 对象的类型
	@Override
	public Class<?> getObjectType() {
		
		return Car.class;
	}
 

Car.java

public class Car {
	private int id;
	private String mark;   //品牌
	//get/set/toString,方法
}

factoryBean.xml

   <!-- 通过FactoryBean实现类CarFactoryBean,来注入一个宝码的Car实例对象 -->
   <bean id="bwm" class="cn.ybzy.springdemo.model.CarFactoryBean">
      <constructor-arg  value="BWM"></constructor-arg><!-- 构造方法注入,当然也可属性注入,但我没有写type的set方法 -->
   </bean>

测试类中:

@Test
	public void FactoryBeantest() {
		ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
		Car baomaCar = (Car) ct.getBean("bwm");
		System.out.println(baomaCar);
	}
----------------------------------------------------------------------------------------------------------------
Car [id=101, mark=BWM]

通过整合Quartz框架-定时做事用的,来演示看看,实际项目应用中,是怎么用FactoryBean来将第三方框架整合进入spring的!
1、导入spring-context-support,quartz等相关jar包

<dependency>
	    <groupId>org.quartz-scheduler</groupId>
	    <artifactId>quartz</artifactId>
	    <version>2.2.1</version>
	</dependency>
	<dependency>
	   <groupId>org.springframework</groupId>
	   <artifactId>spring-context-support</artifactId>
	   <version>5.0.6.RELEASE</version>
	</dependency>
	<dependency>
       <groupId>org.slf4j</groupId>
       <artifactId>slf4j-api</artifactId>
       <version>1.7.35</version>
   </dependency>
   <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-nop -->
   <dependency>
     <groupId>org.slf4j</groupId>
      <artifactId>slf4j-nop</artifactId>
      <version>1.7.35</version>
     <scope>test</scope>
</dependency>

2、定义一个工作任务job

public class MyJob implements Job{

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		System.out.println("quartz我的具体的每次工作任务");
		
	}

}

3、Spring配置文件中整合配置:
1)定义工作任务的Job
2)定义触发器Trigger,并将触发器与工作任务绑定
3)定义调度器,并将Trigger注册到Scheduler

!-- 定义工作任务的Job bean -->
    <bean id="myJob" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
       <!-- 指定job的名称 -->
       <property name="name" value="myQuartzJob"></property>
       <!-- 指定job的分组 -->
       <property name="group" value="myJobGroup"></property>
       <!-- 指定具体的job类 -->
       <property name="jobClass" value="cn.ybzy.springdemo.model.MyJob"></property>
       <!-- 必须置为true,如果为false ,当没有活动的触发器与之关联时会在调度器中会删除该任务 -->
       <property name="durability" value="true"></property>
       <!--springIOC容器的KEY  -->
       <property name="applicationContextJobDataKey" value="factoryBean"></property>
       
    </bean>
    
      <!-- 定义触发器Trigger,并将触发器与工作任务绑定 -->
    <bean id="trigger"  class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
        <property name="name" value="myTrigger"></property>
        <property name="group" value="myGroup"></property>
        <!-- 指定Trigger绑定的job -->
        <property name="jobDetail" ref="myJob"></property>
        <!-- 指定Cron的表达式,当前是每隔3秒运行一次 -->
        <property name="cronExpression" value="0/3 * * * * ?"></property>
    </bean>
    
     <!-- 调度器,并把Trigger注册到Scheduler -->
     <bean id="scheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
        <property name="triggers">
          <list>
             <ref bean="trigger"/>
          </list>
        </property>
     </bean>

一个cron表达式有至少6个(也可能7个)有空格分隔的时间元素。

CronTrigger配置完整格式为: [秒] [分] [小时] [日] [月] [周] [年]

*表示所有值. 例如:在分的字段上设置 "*",表示每一分钟都会触发。

? 表示不指定值。使用的场景为不需要关心当前设置这个字段的值。
      例如:要在每月的10号触发一个操作,但不关心是周几,
      所以需要周位置的那个字段设置为"?" 具体设置为 0 0 0 10 * ?
      
- 表示区间。例如 在小时上设置 "10-12",表示 10,11,12点都会触发。

, 表示指定多个值,例如在周字段上设置 "MON,WED,FRI" 表示周一,周三和周五触发

/ 用于递增触发。如在秒上面设置"5/15" 表示从5秒开始,每增15秒触发(5,20,35,50)。

测试

	//FactoryBean测试,FactoryBean quartz定时任框架测试,但在junit不支持多线程,所以看不到每三秒执行的效果,我
	//另在model层中写一个类MyJob.java,用了main方法
	@Test
	public void FactoryBeantest() {
//		ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
//		Car baomaCar = (Car) ct.getBean("bwm");
//		System.out.println(baomaCar);
		
		ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
	    SchedulerFactoryBean scheduler=(SchedulerFactoryBean)ct.getBean("scheduler");
	    scheduler.start();
	}

MyJobTest.java—model层中(无所谓那层了)

public class MyJobTest {
	public static void main(String[] args) {
		ConfigurableApplicationContext ct = new ClassPathXmlApplicationContext("factoryBean.xml");
	    SchedulerFactoryBean scheduler=(SchedulerFactoryBean)ct.getBean("scheduler");
	    scheduler.start();
	}

}
----------------------------------------------------------------------------------------------------------------
quartz我的具体的每次工作任务
quartz我的具体的每次工作任务
quartz我的具体的每次工作任务
。。。。。。。。。

注解配bean

       Spring引入了注解,通过"@XXX"的方式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。
Spring注解配置初始化对象(<bean>):
spring中使用注解配置对象前,要在配置文件中配置<context:component-scan >标签告诉spring框架,配置了注解的类的位置配置文件applicationContext.xml:

<!--告诉sping,要创建的bean在springdemo包下及子包中所有类的bean-->
<context:component-scan  base-package="cn.ybzy.springdemo">
</context:component-scan>

注解说明:

       Component是最初spring框架设计的,后来为了标识不同代码层,衍生出Controller,Service,Repository三个注解 作用相当于配置文件的bean标签,被注解的类,spring始化时,就会创建该对象

@Component("user")  给类注解
@Service("user") // service层
@Controller("user") // web业务层
@Repository("user")//dao层
@Scope(scopeName="singleton") 等同于配置文件的scope属性

@Value(value="188") //给值属性赋值,可以用在方法上或者属性上
@Resource(name="car") //给对象赋值,该值car必须要已经声明(在配置文件中已经配置,或者在类对应中已经注解)
@PostConstruct //指定该方法在对象被创建后马上调用 相当于配置文件中的init-method属性
@PreDestroy //指定该方法在对象销毁之前调用 相当于配置文件中的destory-method属性
@Autowired 用的非常多
       //自动装配对象赋值@Qualifier("car2") 一起使用 告诉spring容器自动装配哪个对象

例:Tiger.java,Monkey.java,Zoo.java,zhujieBean.xml,RunTest.java


@Component("tiger")
public class Tiger {
	@Value(value="东南虎")
	private String name;

	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "Tiger [name=" + name + "]";
	}
}

@Component("monkey")
public class Monkey {
	@Value("金丝猴")
	private String name;

	public String getName() {
		return name;
	}

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

	@Override
	public String toString() {
		return "Monkey [name=" + name + "]";
	}
	

}
@Component
public class Zoo {
	@Resource(name="tiger")
	private Tiger tiger;
	@Resource(type=Monkey.class)
	private Monkey monkey;
	public Tiger getTiger() {
		return tiger;
	}
	public void setTiger(Tiger tiger) {
		this.tiger = tiger;
	}
	public Monkey getMonkey() {
		return monkey;
	}
	public void setMonkey(Monkey monkey) {
		this.monkey = monkey;
	}
	@Override
	public String toString() {
		return "Zoo [tiger=" + tiger.getName() + ", monkey=" + monkey.getName() + "]";
	}
	

}


  <!-- 注解配bean -->
    <!-- 1.先非注解配bean(对象),让我们作个比较 -->
   <!-- 
    <bean name="tiger" class="cn.ybzy.springdemo.model.Tiger" p:name="东南虎"></bean>
    <bean name="monkey" class="cn.ybzy.springdemo.model.Monkey" p:name="金丝猴"></bean>
    <bean name="nanchangzoo" class="cn.ybzy.springdemo.model.Zoo" p:monkey-ref="monkey" p:tiger-ref="tiger"></bean>  
   -->
    <!-- 2.注解配bean(对象),让我们作个比较 -->
    <context:component-scan base-package="cn.ybzy.springdemo.model"></context:component-scan>

测试

//注解配bean测试
	@Test
	public void zhujieBeantest() {
		ApplicationContext ct = new ClassPathXmlApplicationContext("zhujieBean.xml");
		Tiger tiger=(Tiger)ct.getBean("tiger");
		System.out.println(tiger);
		//Zoo zoo = (Zoo)ct.getBean("nanchangzoo");
		Zoo zoo = (Zoo)ct.getBean("zoo");
		System.out.println(zoo);
		
	}
----------------------------------------------------------------------------------------------------------------
Tiger [name=东南虎]
Zoo [tiger=东南虎, monkey=金丝猴]

说一下@Resource的装配顺序:

1、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
2、指定了name或者type则根据指定的类型去匹配bean
3、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错

@Autowired

       @Autowired顾名思义,就是自动装配,其作用是为了消除代码Java代码里面的getter/setter与bean属性中的property,(如:Zoo,可以不用setTiger,setMonkey方法)。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。
因此,引入@Autowired注解,不要忘记配置文件要写:

<context:component-scan  base-package="cn.ybzy.springdemo">
</context:component-scan>

然后才是在JavaBean的属性上加注解:
在这里插入图片描述
       如果属性找不到对应的对象我不想让Spring容器抛 出异常,而就是显示null,可以吗?可以的,就是将@Autowired注解的required属性设置为false 即可:
在这里插入图片描述

@Autowired接口注入
       上面的比较简单,我们只是简单注入一个Java类,那么如果有一个接口,有多个实现,Bean里引用的是接口名,又该怎么做呢?比如有一个Car接口:
       Car接口有两个实现类,Spring并不知道应当引用哪个实现类。这种情况通常有两个解决办法:

  1. 删除其中一个实现类,Spring会自动去base-package下寻找Car接口的实现类,发现Car接口只有一个实现类,便会直接引用这个实现类
  2. 实现类就是有多个该怎么办?此时可以使用@Qualifier注解,指明你要spring装载那个对象:
    在这里插入图片描述
    最后提一下,还有一个功能和@Autowired差不多的一个注解@inject,它是jsr330规范的注解,用它的话要导入相应的jar包,我们推荐使用@Autowired
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qq_33406021

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值