服务端三层开发:表现层、业务层、持久层 ssm, springboot, springcloud(微服务,治理组件)
Spring框架是一个流行的Java应用程序框架,它提供了许多功能来简化企业级应用程序的开发。其中,控制反转(Inversion of Control,IoC)和面向切面编程(Aspect-Oriented Programming,AOP)是Spring框架的两个重要概念。
1.控制反转(IoC):将对象的创建权力反转给Spring框架
控制反转是一种设计原则,旨在将控制权从代码中提取出来,并交由外部容器或框架来管理。在传统的程序设计中,对象之间的依赖关系通常是在代码中硬编码的,这会导致代码之间的耦合度较高,不利于维护和扩展。通过控制反转,依赖关系的管理被抽象到一个外部容器或框架中,从而降低了代码之间的耦合度。
在Spring框架中,IoC通过依赖注入(Dependency Injection,DI)的方式来实现。DI是一种将一个对象的依赖关系注入到该对象中的技术。通过使用IoC容器,开发者可以声明对象的依赖关系,而IoC容器会在运行时自动将这些依赖关系注入到对象中。这种方式使得对象的创建和依赖关系的配置更加灵活和可维护。
2. 面向切面编程(AOP):
面向切面编程是一种编程范式,旨在将应用程序中的横切关注点(cross-cutting concerns)从业务逻辑中分离出来。横切关注点是指跨多个模块或组件的通用功能,例如日志记录、事务管理、安全等。在传统的程序设计中,这些横切关注点通常会与业务逻辑混杂在一起,导致代码难以维护和扩展。
AOP通过定义“切面”来将横切关注点从业务逻辑中分离出来。一个切面可以定义为一个跨越多个模块或组件的横切关注点的集合。通过使用AOP,开发者可以将通用功能封装在独立的模块或组件中,并在需要时将其应用于业务逻辑。这种方式可以提高代码的可维护性和可重用性,并减少代码的冗余。
在Spring框架中,AOP通过代理模式来实现。代理模式是一种设计模式,其中客户端通过代理对象来访问目标对象。通过使用代理模式,Spring框架可以在运行时动态地将横切关注点应用到业务逻辑中。
1.方便解耦,简化开发,Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理。IOC的作用。
2.AOP编程的支持,Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。(可扩展性)
3.声明式事务的支持,只需要通过配置就可以完成对事务的管理,而无需手动编程。
4.方便程序的测试,Spring对Junit4支持,可以通过注解方便的测试Spring程序。
5.方便集成各种优秀框架,Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts2、Hibernate、MyBatis、Quartz等)的直接支持。
6.降低JavaEE API的使用难度,Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。
创建Java工程,导入坐标依赖pom.xml
org.springframework
spring-context
5.0.2.RELEASE
commons-logging
commons-logging
1.2
log4j
log4j
1.2.12
junit
junit
4.12
test
编写接口和实现类,编写具体的实现方法
package com.qcby.service;
public interface UserService {
public void hello();
}
package com.qcby.service;
public class UserServiceImpl implements UserService {
@Override
public void hello() {
System.out.println("Hello IOC!!");
}
}
编写Spring核心的配置文件,在src目录下创建applicationContext.xml的配置文件,名称是可以任意的,但是一般都会使用默认名称。
编写测试方法
package com.qcby.test;
import com.qcby.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo {
/**
* 入门程序
*/
@Test
public void run(){
// 使用Spring的工厂
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
// 通过工厂获得类:
UserService userService = (UserService) applicationContext.getBean("userService");
userService.hello();
}
}
IOC技术总结
ApplicationContext接口,工厂的接口,使用该接口可以获取到具体的Bean对象。该接口下有两个具体的实现类。
ClassPathXmlApplicationContext,加载类路径下的Spring配置文件。
FileSystemXmlApplicationContext,加载本地磁盘下的Spring配置文件。
id属性,Bean起个名字,在约束中采用ID的约束,唯一,取值要求:必须以字母开始,可以使用字母、数字、连字符、下划线、句话、冒号 id:不能出现特殊字符。
class属性,Bean对象的全路径。
scope属性,scope属性代表Bean的作用范围。 singleton单例(默认值),最常用的方式; prototype多例
request应用在Web项目中,每次HTTP请求都会创建一个新的Bean
session应用在Web项目中,同一个HTTP Session 共享一个Bean
Bean对象的创建和销毁的两个属性配置
说明:Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法
init-method,当bean被载入到容器的时候调用init-method属性指定的方法
destroy-method,当bean从容器中删除的时候调用destroy-method属性指定的方法
单例的对象销毁:跟着容器工厂关闭才销毁
多例的对象销毁:垃圾回收机制进行回收的
实例化Bean对象的三种方式
①默认是无参数的构造方法(默认方式,基本上使用)
②静态工厂实例化方式
package com.qcby.demo1;
import com.qcby.service.UserService;
import com.qcby.service.UserServiceImpl;
/**
* 静态工厂方式
*/
public class StaticFactory {
// 静态工厂方式
public static UserService createUs(){
System.out.println("通过静态工厂的方式创建UserServiceImpl对象...");
// 编写很多业务逻辑 权限校验
return new UserServiceImpl();
}
}
③动态工厂实例化方式
package com.qcby.demo1;
import com.qcby.service.UserService;
import com.qcby.service.UserServiceImpl;
/**
*
* 动态工厂方式
*
*/
public class Dfactory {
public UserService createUs(){
System.out.println("实例化工厂的方式...");
return new UserServiceImpl();
}
}
IOC和DI的概念
IOC:Inverse of Control,控制反转,将对象的创建权反转给Spring
DI:Dependency Injection,依赖注入,在Spring框架负责创建Bean对象时,动态的将依赖对象注入到Bean组件中
①属性的set方法注入值
编写属性,提供该属性对应的set方法,编写配置文件完成属性值的注入
package com.qcby.service;
public class Person {
// 编写成员属性,一定需要提供该属性的set方法
private String name;
private Integer age;
private Car car;
// 一定需要提供该属性的set方法,IOC容器底层就通过属性的set方法方式注入值
public void setName(String name) {
this.name = name;
}
public void setAge(Integer age) {
this.age = age;
}
public void setCar(Car car) {
this.car = car;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", car=" + car +
'}';
}
}
package com.qcby.service;
public class Car {
// 名称
private String cname;
// 金额
private Double money;
public void setCname(String cname) {
this.cname = cname;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Car{" +
"cname='" + cname + '\'' +
", money=" + money +
'}';
}
}
②属性构造方法方式注入值
对于类成员变量,构造函数注入。
package com.qcby.domain;
public class Car {
// 名称
private String cname;
// 金额
private Double money;
public Car(String cname, Double money) {
this.cname = cname;
this.money = money;
}
@Override
public String toString() {
return "Car{" +
"cname='" + cname + '\'' +
", money=" + money +
'}';
}
}
③数组,集合(List,Set,Map),Properties等的注入
package com.qcby.domain;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class CollectionBean {
// 数组
private String [] strs;
public void setStrs(String[] strs) {
this.strs = strs;
}
private List list;
public void setList(List list) {
this.list = list;
}
private Map map;
public void setMap(Map map) {
this.map = map;
}
private Properties properties;
public void setProperties(Properties properties) {
this.properties = properties;
}
@Override
public String toString() {
return "CollectionBean{" +
"strs=" + Arrays.toString(strs) +
", list=" + list +
", map=" + map +
", properties=" + properties +
'}';
}
}
美美
小凤
熊大
熊二
root
123456
在src的目录下又多创建了一个配置文件,现在是两个核心的配置文件,那么加载这两个配置文件的方式有两种!
主配置文件中包含其他的配置文件:
工厂创建的时候直接加载多个配置文件:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"applicationContext.xml","applicationContext2.xml");
① Spring框架开发方式
1) 需求:编写service和dao的类,演示代码
2) 技术选择:持久层使用原始的JDBC的程序,连接池选择的是Druid连接池。创建maven工程,导入开发的jar包
org.springframework
spring-context
5.0.2.RELEASE
commons-logging
commons-logging
1.2
junit
junit
4.12
test
com.alibaba
druid
1.1.10
mysql
mysql-connector-java
5.1.6
创建数据库,创建表结构
create database spring_db;
use spring_db;
create table account(
id int primary key auto_increment,
name varchar(40),
money double
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
编写JavaBean的类
package com.qcby.domain;
import java.io.Serializable;
public class Account implements Serializable {
private static final long serialVersionUID = 7355810572012650248L;
private Integer id;
private String name;
private Double money;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
}
编写AccountDao的接口和实现类
package com.qcby.dao;
import com.qcby.domain.Account;
import java.util.List;
public interface AccountDao {
public List findAll();
}
package com.qcby.dao;
import com.qcby.domain.Account;
import com.alibaba.druid.pool.DruidDataSource;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
public class AccountDaoImpl implements AccountDao {
//不使用连接池
//1、加载驱动
//2、获取连接
//3、编写sql
//4、预编译sql
//5、设置值
//6、执行sql 拿到结果集
//7、遍历结果集关闭资源
// String driver = "com.mysql.jdbc.Driver";
// String url="jdbc:mysql://localhost:3306/spring_db?useUnicode=true&characterEncoding=UTF-8";
// String user = "root";
// String password = "123456";
// String sql = "select * from account";
// List res = new ArrayList();
// Class.forName(driver);
// Connection conn = (Connection) DriverManager.getConnection(url, user, password);
// Statement stmt = (Statement) conn.createStatement();
// ResultSet rs = (ResultSet) stmt.executeQuery(sql);
// while(rs.next()){
// Account acc = new Account();
// acc.setId(rs.getInt("id")); //获取id字段的值并设置到Account对象中
// acc.setName(rs.getString("name")); //获取name字段的值并设置到Account对象中
// acc.setMoney(rs.getDouble("money")); //获取money字段的值并设置到Account对象中
// res.add(acc);
// }
// conn.close();
// stmt.close();
// rs.close();
// return res;
// 注入连接池对象
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* 查询所有的数据
* @return
*/
@Override
public List findAll() {
/*
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("123456");
*/
List list = new ArrayList<>();
Connection connection = null;
PreparedStatement stmt = null;
ResultSet rs = null;
try {
// 获取连接
connection = dataSource.getConnection();
// 编写sql语句
String sql = "select * from account";
// 预编译
stmt = connection.prepareStatement(sql);
// 查询
rs = stmt.executeQuery();
// 遍历,封装数据
while (rs.next()){
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getDouble("money"));
list.add(account);
}
} catch (SQLException e) {
e.printStackTrace();
}finally {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
return list;
}
}
package com.qcby.service;
import com.qcby.domain.Account;
import java.util.List;
public interface AccountService {
public List findAll();
}
package com.qcby.service;
import com.qcby.dao.AccountDao;
import com.qcby.domain.Account;
import java.util.List;
public class AccountServiceImpl implements AccountService {
// 依赖注入
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
/**
* 查询所有的数据
* @return
*/
@Override
public List findAll() {
return accountDao.findAll();
}
}
编写配置文件
编程测试程序
package com.qcby.test;
import com.qcby.domain.Account;
import com.qcby.service.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class Demo1 {
@Test
public void run1(){
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService accountService = (AccountService) ac.getBean("accountService");
// 调用方法
List list = accountService.findAll();
for (Account account : list) {
System.out.println(account);
}
}
}
① IOC注解方式的简单示例
IOC注解的方式依赖没有变化
编写接口和实现类
package com.qcby.service;
public interface UserService {
public void hello();
}
package com.qcby.service;
import org.springframework.stereotype.Component;
/**
*
*/
// 组件,作用:把当前类使用IOC容器进行管理,如果没有指定名称,默认使用类名,首字母是小写。userServiceImpl。或者自己指定名称
@Component(value = "us")
public class UserServiceImpl implements UserService {
@Override
public void hello() {
System.out.println("Hello IOC注解...");
}
}
在需要管理的类上添加@Component注解
@Component(value = "us")
public class UserServiceImpl implements UserService {
@Override
public void hello() {
System.out.println("Hello IOC注解...");
}
}
编写配置文件,重点是开启注解扫描。
编写测试方法
package com.qcby.test;
import com.qcby.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo2 {
/**
* IOC注解方式的入门
*/
@Test
public void run1(){
// 工厂
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_anno.xml");
// 获取对象
UserService userService = (UserService) ac.getBean("us");
userService.hello();
}
}
② 常用的注解
bean管理类常用的4个注解(作用相同,推荐使用在不同分层上)
@Component 普通的类,任何地方
@Controller 表现层
@Service 业务层
@Repository 持久层
依赖注入常用的注解
@Value 用于注入普通类型(String,int,double等类型),可以省略
@Autowired 默认按类型进行自动装配(引用类型)
@Qualifier 和@Autowired一起使用,强制使用名称注入
@Resource Java提供的注解,也被支持。使用name属性,按名称注入对象生命周期(作用范围)注解
@Scope 生命周期注解,取值singleton(默认值,单实例)和prototype(多例)
初始化方法和销毁方法注解(了解)
@PostConstruct 相当于init-method
@PreDestroy 相当于destroy-method
单例的销毁时机是跟随容器的 容器销毁对象销毁
多例的销毁时机是java的垃圾回收机制控制的
具体的代码如下
package com.qcby.domain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
// 默认当前类名就是ID名称,首字母小写
@Component(value = "c")
// @Controller
// @Service(value = "c")
// @Repository(value = "c")
// @Scope(value = "singleton") // 默认值,单例的
// @Scope(value = "prototype") // 多例的
public class Car {
// 注解注入值,属性set方法是可以省略不写的。
// 只有一个属性,属性的名称是value,value是可以省略不写的
@Value("大奔2")
private String cname;
@Value(value = "400000")
private Double money;
// 也不用提供set方法
// 按类型自动装配的注解,和id名称没有关系
@Autowired
// 按id的名称注入,Qualifier不能单独使用,需要Autowired一起使用。
// @Qualifier(value = "person")
// @Resource Java提供的注解,按名称注入对象,属性名称是name
// @Resource(name = "person")
private Person person;
/**
* Car对象创建完成后,调用init方法进行初始化操作
*/
@PostConstruct
public void init(){
System.out.println("操作...");
}
/*
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
*/
@Override
public String toString() {
return "Car{" +
"cname='" + cname + '\'' +
", money=" + money +
", person=" + person +
'}';
}
}
package com.qcby.domain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component(value = "person")
public class Person {
@Value("张三")
private String pname;
@Override
public String toString() {
return "Person{" +
"pname='" + pname + '\'' +
'}';
}
}
package com.qcby.test;
import com.qcby.service.UserService;
import com.qcby.domain.Car;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo3 {
@Test
public void run1(){
// 工厂
ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext_anno.xml");
// 获取对象
Car car = (Car) ac.getBean("c");
System.out.println(car);
}
}
③ IOC纯注解的方式
纯注解的方式是微服务架构开发的主要方式,所以也是非常的重要。纯注解的目的是替换掉所有的配置文件。但是需要编写配置类。
编写实体类
package com.qcby.domain;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class Order {
@Value("北京")
private String address;
@Override
public String toString() {
return "Order{" +
"address='" + address + '\'' +
'}';
}
}
编写配置类,替换掉applicationContext.xml配置文件
package com.qcby.domain;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Spring的配置类,替换掉applicationContext.xml
*
*/
// 声明当前类是配置类
@Configuration
// 扫描指定的包结构
@ComponentScan(value = "com.qcby")
public class SpringConfig {
}
测试方法的编写
package com.qcby.test;
import com.qcby.domain.Order;
import com.qcby.domain.SpringConfig;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo4 {
/**
* 编写程序,需要加载配置类
*/
@Test
public void run(){
// 创建工厂,加载配置类
ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfig.class);
// 获取到对象
Order order = (Order) ac.getBean("order");
System.out.println(order);
}
}
常用的注解总结
@Configuration 声明是配置类
@ComponentScan 扫描具体包结构的
@Import注解 Spring的配置文件可以分成多个配置的,编写多个配置类。用于导入其他配置类
package com.qcby.demo4;
import org.springframework.context.annotation.Configuration;
/**
* 新的配置类
*
*/
@Configuration // 声明配置类
public class SpringConfig2 {
}
// 声明当前类是配置类
@Configuration
// 扫描指定的包结构
@ComponentScan(value = "com.qcby.demo4")
// @ComponentScan(value = {"com.qcby.demo4","com.qcby.demo3"})
// 引入新的配置类
@Import(value = {SpringConfig2.class})
public class SpringConfig {
}
@Bean注解 只能写在方法上,表明使用此方法创建一个对象,对象创建完成保存到IOC容器中
package com.qcby.demo4;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import javax.sql.DataSource;
/**
*
* Spring的配置类,替换掉applicationContext.xml
*
*/
// 声明当前类是配置类
@Configuration
// 扫描指定的包结构
@ComponentScan(value = "com.qcby.demo4")
// @ComponentScan(value = {"com.qcby.demo4","com.qcby.demo3"})
// 引入新的配置类
@Import(value = {SpringConfig2.class})
public class SpringConfig {
/**
* 创建连接池对象,返回对象,把该方法创建后的对象存入到连接池中,使用@Bean注解解决
*
* @return
*/
@Bean(name="dataSource")
public DataSource createDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql:///spring_db");
dataSource.setUsername("root");
dataSource.setPassword("123456");
return dataSource;
}
}
① Spring框架整合JUnit单元测试
每次进行单元测试的时候,都需要编写创建工厂,加载配置文件等代码,比较繁琐。Spring提供了整合Junit单元测试的技术,可以简化测试开发。
必须先有Junit单元测试的环境,也就是说已经导入Junit单元测试的jar包。咱们已经导入过了。使用的是4.12版本
再导入spring-test的坐标依赖
org.springframework
spring-test
5.0.2.RELEASE
test
编写类和方法,把该类交给IOC容器进行管理
package com.qcby.demo5;
public class User {
public void sayHello(){
System.out.println("Hello....");
}
}
编写配置文件applicationContext_test.xml
编写测试代码
package com.qcby.test;
import com.qcby.demo5.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Spring整合Junit单元测试
*
*/
@RunWith(value = SpringJUnit4ClassRunner.class) // 运行单元测试
@ContextConfiguration(value = "classpath:applicationContext_test.xml") // 加载类路径下的配置文件
public class Demo5 {
// 测试哪一个对象,把该对象注入进来,在测试环境下,可以使用注解的方式注入测试的对象
// 按类型自动注入
@Autowired
private User user;
@Test
public void run1(){
// 创建工厂,加载配置文件......
// 调用对象的方法
user.sayHello();
}
}
②Spring整合单元测试(纯注解方式)
编写类和方法
package com.qcby.demo6;
import org.springframework.stereotype.Component;
@Component
public class Customer {
public void save(){
System.out.println("保存客户...");
}
}
编写配置类
package com.qcby.demo6;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* Spring整合Junit配置类
*/
// 声明
@Configuration
// 扫描包结构
@ComponentScan(value = "com.qcby.demo6")
public class SpringConfig6 {
}
编写测试方法
package com.qcby.test;
import com.qcby.demo6.Customer;
import com.qcby.demo6.SpringConfig6;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Spring整合Junit 注解的方式测试
*/
@RunWith(SpringJUnit4ClassRunner.class)
// 加载配置类
@ContextConfiguration(classes = SpringConfig6.class)
public class Demo6 {
// 按类型注入
@Autowired
private Customer customer;
/**
* 测试
*/
@Test
public void run(){
customer.save();
}
}