Spring_Bean配置_生命周期_注解

Spring

Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。

Spring核心

1.控制反转(IOC)
2.面向切面(AOP)

Spring优点

1.解耦, 简化开发: Spring就是一个工厂,可以将所有对象创建和依赖关系维护交给Spring处理
2.AOP支持: 方便实现事务处理, 权限处理, 监控操作
3.声明式事务支持: 只需通过配置就可以完成事务管理
4.提供Junit支持
5.集成其他框架
6.对JavaEE API的封装, 降低开发难度

Spring体系结构

Spring是一个分层架构, 包含的功能可分为大约20个模块.

Core Container(核心容器):Beans: 管理BeansCore: Spring核心Context: 配置文件ExpressionLanguage: SpEL表达式
AOP(切面编程)
AOP框架: Aspects
Data Access(数据库整合):JDBC, ORM, OXM, JMS, Transaction
Web(MVC Web开发):Web, Servlet, Portlet, Struts
Test(Junit整合)

IOC(控制反转)

IOC(控制反转): 将创建对象实例交给Spring处理, 每次从Spring工厂中获得对象
下面使用一个简单的例子说明:

目标类:

public interface UserService{void addUser();
}
public class UserServiceImpl implements UserService {public void addUser() {System.out.println("ico add user");}
}

Spring的配置文件:
配置文件名, 放置的位置都是任意的, 一般将配置文件命名为applicationContext.xml, 放置在src下

src目录下的applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!--xsd约束-->
<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">
<!--id: Spring帮你new 的那个对象名class: 那个对象的全类名
-->
<bean id="userService" class="com.a_ioc.UserServiceImpl"></bean>
</beans>

测试代码:

public class TestIoc {@Testpublic void f1(){//XMLPATH代表配置文件所在的位置String XMLPATH="applicationContext.xml";//使用ClassPathXMLApplicationContext(String path)加载指定位置的配置文件ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);//使用applicationContext的getBean获得配置文件中id为userService的对象UserService userService = (UserService) applicationContext.getBean("userService");userService.addUser();}
}

测试方法中不再使用new的方式获得对象, 而是通过控制反转, 将new的行为交给Spring处理(反射机制)

DI(依赖注入)

DI依赖注入: 对实例对象的属性赋值, 一般通过set方法进行反射赋值.
对象的属性一般指另一个对象(就有依赖一说)
下面使用例子说明DI

User的Service层:

public interface UserService{void addUser();
}public class UserServiceImpl implements UserService {//对UserDao对象做依赖注入, 在后面生成get/set方法private UserDao userDao;public void addUser() {userDao.save();}public UserDao getUserDao() {return userDao;}public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}

User的Dao层:

public interface UserDao {void save();
}public class UserDaoImpl implements UserDao {public void save() {System.out.println("di save");}
}

applicationContext.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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--property标签用于依赖注入:将ref映射的对象注入到name中name:需要注入的属性名ref:映射到UserDao对象
--><bean id="userService" class="com.b_di.UserServiceImpl"><property name="userDao" ref="userDao"></property></bean><bean id="userDao" class="com.b_di.UserDaoImpl"></bean>
</beans>

测试:
测试中代码与前面一样几乎不变, 对象的生成, 对象属性的赋值都交给Spring处理, 高度解耦

public class TestDi {@Testpublic void f1(){String XMLPATH="applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService");userService.addUser();}
}

Spring核心API

类结构图

顶级接口: BeanFactory用于生成任意的Bean, 采取延迟加载策略, 使用getBean才会初始化BeanBeanFactory子接口: ApplicationContext功能加强, 使用该接口, 当配置文件被加载的时候, 就进行对象实例化ApplicationContext子接口:1.ClassPathXmlApplicationContext用于加载src下的xml运行时, xml在/WEB-INF/classes/...xml2.FileSystemXmlApplicationContext加载指定盘符下的xml运行时, xml在/WEB-INF/...xml 

举例说明BeanFactory

public class TestBeanFactory {@Testpublic void f1(){String XMLPATH="applicationContext.xml";//加载完配置文件后, 并不会实例化BeanFactory applicationContext = new XmlBeanFactory(new ClassPathResource(XMLPATH));UserService userService = (UserService) applicationContext.getBean("userService");userService.addUser();}
}

调用ClassPathXmlApplicationContext(XMLPATH)加载配置文件, Spring就会调用配置文件中的配置的bean的构造函数, 而XMLBeanFactory(Resource)不会, 只是单纯加载配置文件, 只有当getBean才会调用构造函数

基于XML装配Bean

3种Bean实例化方式:
默认构造获得Bean, 使用静态工厂获得Bean, 使用实例工厂获得Bean

  • 使用默认构造函数:
使用如下标签配置Bean
<bean id="..." class="..."></bean>
注: 该Bean必须有默认构造函数(使用默认构造进行反射生成)
  • 静态工厂:
<bean id="" class="工厂全类名" factory-method="静态方法"></bean>举例说明:
静态工厂:public class StaticBeanFactory {public static UserService newUserService(){return  new UserServiceImpl();}}
测试静态工厂:
public class TestStaticFactory {@Testpublic void f1(){String XMLPATH="applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService1", UserService.class);userService.addUser();}
}application.xml:<bean id="userService1" class="com.c_createBean.StaticBeanFactory" factory-method="newUserService"></bean>
  • 实例工厂:
    先获得工厂的实例对象, 然后通过实例对象创建想要的对象, 实例工厂提供的方法都是非静态方法
实例工厂:
public class MyBeanFactory {public UserService newUserService(){System.out.println("MyBeanFactory newUserService");return  new UserServiceImpl();}
}测试方法:
@Testpublic void f1(){String XMLPATH="applicationContext.xml";ApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService2", UserService.class);userService.addUser();}applicationContext.xml:
<!--获得工厂实例-->
<bean id="myBeanFactory" class="com.c_createBean.MyBeanFactory"></bean>
<!--通过工厂实例调用方法获得指定对象-->
<bean id="userService2" factory-bean="myBeanFactory" factory-method="newUserService"></bean>

Spring中Bean种类

普通Bean: Spring直接创建实例<bean id="普通Bean名" class="普通Bean"></bean>FactoryBean: 生产特定Bean的工厂,可以是静态/非静态工厂
非静态举例<bean id="工厂对象名" class="工厂类"></bean><bean id="特定Bean" factory-bean="工厂对象名" factory-method="工厂方法名">BeanFactory: 使用动态代理生成任意的Bean

Bean作用域

确定Spring创建Bean实例个数, 在bean标签中使用scope属性确定

singleton(默认值):在Spring中只存在一个Bean实例, 单例模式.
prototype:getBean()的时候都会new Bean(), 多例
request:每次http请求都会创建一个Bean, 仅用于WebApplicationContext环境
session:同一个http session共享一个Bean, 不同Session使用不同的Bean, 使用环境同上
globalSession:用于Portlet, 环境同上<bean id="..." class="..." scope="..."></bean>

Spring生命周期

生命周期详情:

1.instantiate bean对象实例化
2.populate properties 封装属性
3.如果Bean实现BeanNameAware 执行 setBeanName
4.如果Bean实现BeanFactoryAware 或者 ApplicationContextAware 设置工厂 setBeanFactory 或者上下文对象 setApplicationContext
5.如果存在类实现 BeanPostProcessor(后处理Bean) ,执行postProcessBeforeInitialization
6.如果Bean实现InitializingBean 执行 afterPropertiesSet 
7.调用<bean init-method="init"> 指定初始化方法 init
8.如果存在类实现 BeanPostProcessor(处理Bean) ,执行postProcessAfterInitialization
9.执行业务处理
10.如果Bean实现 DisposableBean 执行 destroy
11.调用<bean destroy-method="customerDestroy"> 指定销毁方法 customerDestroy
  • 初始化与销毁
    在构造方法前->初始化, 容器关闭时->销毁
bean标签写法:
<bean id="..." class="..." init-method="初始化方法名" destory-method="销毁方法名">
实现初始化与销毁必须满足:容器必须close, 此时销毁方法执行必须是单例

下面使用一个简单例子演示:

applicationContext.xml:<bean id="userService" class="com.e_lifeCycle.UserServiceImpl" init-method="myInit" destroy-method="myDestory"></bean>UserServiceImp.java:
public class UserServiceImpl implements UserService {public void addUser() {System.out.println("lifeCycle add User");}public void myInit() {System.out.println("init");}public void myDestory() {System.out.println("destory");}
}Test:@Testpublic void f1(){String XMLPATH="com/e_lifeCycle/applicationContext.xml";ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(XMLPATH);UserService userService = (UserService) applicationContext.getBean("userService");userService.addUser();//ApplicationContext没有close方法, 使用ClassPathXmlApplicationapplicationContext.close();}
//在执行构造完Bean对象的时候执行myInit
//在容器关闭之前执行myDestory
  • BeanPostProcessor实现AOP, 完成事务处理
    Bean实现BeanPostProcessor, 当Spring管理Bean的时候,
    就能在初始化方法前执行PostProcessAfterInitialization(Object bean, String beanName);
    在初始化方法后执行PostProcessBeforeInitialization(Object bean, String beanName);

下面举例说明:

编写BeanPostProcessor:
public class PostProcessor implements BeanPostProcessor{/*** 执行方法之前调用, 就意味着在初始化的时候就会调用事务处理* 此时的事务处理是针对接口中有的方法*/@Overridepublic Object postProcessBeforeInitialization(final Object bean, String beanName)throws BeansException {System.out.println("方法前"+beanName);return bean;}@Overridepublic Object postProcessAfterInitialization(final Object bean, String beanName)throws BeansException {System.out.println("方法后"+beanName);return Proxy.newProxyInstance(PostProcessor.class.getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {//反射重写invoke, 实现AOP@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {System.out.println("开启事务");Object objectInvoke = method.invoke(bean, args);System.out.println("关闭事务");return objectInvoke;}});}
}在applicationContext.xml中配置:
<bean class="com.e_lifeCycle.PostProcessor"></bean>
注册自定义的BeanPostProcessor测试类:
测试类就是前面的初始化与销毁代码
运行结果:构造方法前userServiceinit方法后userService开启事务lifeCycle add User关闭事务destory

生命周期, 事件执行流程

使用上面的lifeCycle作为例子

1.加载配置文件, 构造方法执行
2 装载属性,调用setter方法
3.通过BeanNameAware接口,获得配置文件id属性的内容:lifeUser
4.通过ApplicationContextAware接口,获得Spring容器
5. 实现BeanPostProcessor后处理,初始化前,执行postProcessBeforeInitialization方法
6.通过InitializingBean,确定属性设置完成之后执行
7.配置init-method执行自定义初始化方法
8. 实现BeanPostProcessor后处理,在自定义初始化之后,执行postProcessAfterInitialization方法
9.通过DisposableBean接口,不需要配置的销毁方法
10.配置destroy-method执行自定义销毁方法

属性依赖注入

1.通过构造方法进行装配:
在bean标签中添加constructor-arg标签进行装配.
例如:

<bean id="..." class="..."><constructor-arg name="username" value="jack"></constructor-arg><constructor-arg index="0" type="java.lang.String" value="jack"></constructor-arg><constructor-arg name="username" ref="userService"></constructor-arg>
</bean>name: 构造函数的参数名称
value: 为属性注入普通类型数据
index: 构造函数第几个参数
type: 构造函数参数的类型, 结合index使用
ref: 为属性注入其他Bean

注: 使用这个参数每次都是调用Bean中首次与标签中数据匹配合适构造函数

2.使用setter方法注入:
就是使用property标签指定name, value/ref值

<bean id="..." class="..."><property name="username" value="jack"></property>
</bean>

P命名空间
主要对"setter方法依赖注入"做简化, 替换property.
在添加P命名空间:
在beans标签中添加: xmlns:p=“http://www.springframework.org/shema/p”

将原来的:<bean id="..." class="..."><property name="username" value="jack"></property></bean>
变为:<bean id="..." class="..." p:name="username" p:value="jack"></bean>

SPEL
用于简化property中属性名, 与属性值的写法, 类似于EL表达式

<property name="属性名" value="#{表达式}"></property>
#{123}, #{'jack'} => 代表数字, 字符串
#{beanId} => 另一Bean的引用
#{beanId.propertyName} => 使用另一个beanId的属性值为当前bean的属性值赋值
#{beanId.clone()} => 使用beanId的方法为bean属性赋值
#{T().字段|方法} => 使用其他类的字段或方法为bean赋值

例:

<property name="cname" value="#{'jack'}"></property>
<property name="cname" value="#{customerId.cname.toUpperCase()}"></property>通过另一个bean,获得属性,调用的方法
<property name="cname" value="#{customerId.cname?.toUpperCase()}"></property>
?. 代表: 如果对象不为null,将调用方法

4.集合注入:
在property标签中注入集合数据

注入数组:
<property name="属性名"><array><value>A</value><value>B</value><value>C</value></array>
</property>注入List/Set只需将<array>改为<list>, <set>注入Map:
<property name="属性名"><map><entry key="1" value="A"></entry><entry><key><value>2</value></key><value>B</value></entry>上面这两种写法效果一样</map>
</property>注入Properties数据:
<property name="propsData"><props><prop key="1">A</prop><prop key="2">B</prop></props>
</property>

基于注解装配Bean

使用注解的前提:

在 beans 标签下添加 context:component-scan标签, 扫描包下所有类的注解
<context:component-scan base-package="com.itheima.g_annotation.a_ioc"></context:component-scan>

取代 bean 标签的注解:

1. @Component("beanId")等价于<bean id="beanId" class="...">@Component("userServiceId")public Class UserServiceImpl implements UserService{//..............	}2. 在web中, 提供3个注解用于标识三层架构, 这3个注解效果与Component一样@Repository: dao层@Service: service层@Controller: web层

取代setter依赖注入的注解:
在类中对象的属性上加注解, 就能完成注入(没有setter方法也可以注入)

1.普通数据类型值: @Value("属性值")@Valueprivate UserService userService;2.引用数据类型值: 按照类型注入: @Autowired@Autowiredprivate UserService userService;按照名称注入: @Autowired@Qualifier("...") 或者 @Resource("...") 两注解效果一样//使用service层生成的userService对象进行注入@Autowired@Qualifier("userService")private UserService userService;3.生命周期: 配置init-method, destroy-method属性初始化:@PostConstruct@PostConstructpublic void myInit(){......}销毁: @PreDestroy@PreDestroypublic void myDestroy(){......}4.作用域: 配置scope属性@Scope("prototype")配置多例

上面有错, 还请指出, 如果认为我写的还不错, 还请点个赞, 多多支持一下, O(∩_∩)O~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/468679.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

完全理解Gson(1):简单入门

GSON是Google开发的Java API&#xff0c;用于转换Java对象和Json对象。本文讨论并提供了使用API的简单代码示例。更多关于GSON的API可以访问&#xff1a;http://sites.google.com/site/gson/. 本文是GSON系列文章的第一篇。本文是其他文章的基础&#xff0c;因此不需要任何GSON…

创业碎碎念

&#xff11;今天&#xff0c;跟几条跟我玩的比较好的篮球狗在讨论人生&#xff0c;其中有一条特别感慨&#xff0c;「为何看上去别人做起来这么容易的事&#xff0c;我们做很难&#xff1f;」。我突然不知道如何去回答这样的问题&#xff1f;这个问题也一直困扰着我。读我文章…

压缩过的js代码怎么还原_Fundebug 前端 JS插件更新至 1.7.0,拆分录屏代码,还原部分 Script error....

摘要&#xff1a; BUG 监控插件压缩至 18K。1.7.0拆分了录屏代码&#xff0c;BUG 监控插件压缩至18K&#xff0c;另外我们还原了部分 Script error&#xff0c;帮助用户更方便地 Debug。请大家及时更新哈~拆分录屏代码从1.7.0版本开始&#xff0c;我们拆分了录屏代码。如果需要…

SpringAOP描述及实现_AspectJ详解_基于注解的AOP实现_SpringJdbcTemplate详解

AOP AOP特点: 面向切面编程, 利用AOP对业务逻辑的各个部分进行抽取公共代码, 降低耦合度, 提高代码重用性, 同时提高开发效率.采取横向抽取, 取代传统纵向继承体系重复性代码解决事务管理, 性能监视, 安全检查, 缓存, 日志等问题Spring AOP在运行期, 通过反向代理的方式解决类…

(十九)java多线程之ForkJoinPool

本人邮箱: kco1989qq.com 欢迎转载,转载请注明网址 http://blog.csdn.net/tianshi_kco github: https://github.com/kco1989/kco 代码已经全部托管github有需要的同学自行下载 引言 java 7提供了另外一个很有用的线程池框架,Fork/Join框架 理论 Fork/Join框架主要有以下两个类组…

串口,com口,ttl,max232你应该知道的事

&#xff11;今天&#xff0c;说几个比较基础的知识&#xff0c;大家在开发过程中经常会遇到但是又不是特别注意的知识点。TTL电平&#xff1a;TTL是Transistor-Transistor Logic&#xff0c;即晶体管-晶体管逻辑的简称&#xff0c;它是计算机处理器控制的设备内部各部分之间通…

Caffe2 的基本数据结构(Basics of Caffe2 - Workspaces, Operators, and Nets)[4]

这篇文章主要介绍Caffe2的基本数据结构&#xff1a; WorkspacesOperatorsNets在开始之前最好先阅读以下Intro Turorial首先&#xff0c;导入caffe2。其中core和worksapce模块&#xff0c;这是必须的两个模块。如果你要使用Caffe2生成的protocol buffers&#xff0c;那么你也需要…

Linux 开发者最应该知道的命令汇总

&#xff11;最近发现一个好东西&#xff0c;在 github 上发现的&#xff0c;我觉得非常适合大家研究 linux&#xff0c;说白了就是一些命令而已&#xff0c;只不过是作者帮忙总结好了&#xff0c;比较适合大家开发和学习 linux 系统&#xff0c;so , 推荐给大家学习下。https:…

华为任职资格_华为采购总部专业任职资格标准|

目 录 序 言 概述 .........................第一部分 级别定义.....................第二部分 资格标准 ....................1、采购工程师&#xff08;生产采购&#xff09;任职资格标准........2、采购员&#xff08;生产采购&#xff09;任职资格标准............3、采购员…

C 语言内存分配

&#xff11;昨天有一个群里的同学问我&#xff0c;他问我的问题是 c 语言函数是存在哪里的&#xff1f;是如何执行的&#xff1f;我下意识的觉得这位同学应该是个初学者&#xff0c;所以今天就写下一些基础方面的内容&#xff0c;「C语言的内存布局」。程序代码可以看做是一个…

没有梦想,你跟咸鱼有什么分别?

&#xff11;标题起的有点夸张&#xff0c;其实这个就是一个招聘贴&#xff0c;之前从来没有用发头条文章来招聘&#xff0c;实在不好意思&#xff0c;这个招聘对我非常重要&#xff0c;这是一个非常好的朋友公司的招聘信息&#xff0c;也希望大家帮忙扩散一下&#xff0c;因为…

一个很Low的通讯录管理系统(但是能用)C/C++单链表实现

通讯录管理系统的设计 问题需求分析 在计算机还未普及之前通讯管理都是由联系人采用名片&#xff0c;通讯录往往采用的是笔录手工记帐的方式来操作的。现在一般的通讯录管理都是采用计算机作为工具的实用的计算机通讯录管理程序来帮助人们进行更有效的通讯录信息管理。本通讯…

2017《面向对象程序设计》课程作业三

作业链接github链接 对于文件读写和多参数主函数学习过程中遇到的问题 这次文件读写改用了C的形式&#xff0c;然后总体还算顺利&#xff0c;借鉴了林燊的&#xff0c;因为他写的代码最容易看懂&#xff1b;还有就是借鉴了《C程序设计》&#xff0c;讲真&#xff0c;谭浩强的还…

华为不做黑寡妇,开源编译器,与友商共建安卓性能

&#xff11;今天我的一个老哥开了头条号&#xff0c;第一次发文章&#xff0c;我觉得不错&#xff0c;拿来用用&#xff0c;给大家看看华为技术总工的文采。这位总工潜伏在我的微信群里很少说话&#xff0c;大家一定要有这个想法&#xff0c;就是最低调的那个人&#xff0c;真…

点击链接如何直接跳转到相对应的聊天窗口

解决这个问题的步骤如下&#xff1a; <a target"_blank" href"http://wpa.qq.com/msgrd?v3&uin3237465337&siteqq&menuyes">一、登陆腾讯官方网站&#xff1a;http://wp.qq.com/ 二、登陆之后&#xff0c;点“设置”&#xff0c;按下图…

哈夫曼树编码与译码(完整C/C++实现代码)

哈夫曼编码的设计与应用 问题需求分析 用哈夫曼编码(Huffman Coding)&#xff0c;又称霍夫曼编码&#xff0c;是一种编码方式&#xff0c;哈夫曼编码是可变字长编码(VLC)的一种。Huffman于1952年提出一种编码方法&#xff0c;该方法完全依据字符出现概率来构造异字头的平均长…

移动应用开发实例_物联网改变移动应用开发的4种方式

图片来源&#xff1a;pixabay.com来源&#xff1a;物联之家网(iothome.com)转载请注明来源&#xff01;物联网改变了移动应用程序的开发格局。那么&#xff0c;为物联网开发移动应用程序有何不同&#xff1f;物联网与移动应用程序开发齐头并进。物联网改变了人类与机器的互动方…

谁都能看懂的网络模型知识

&#xff11;.网络是我们做嵌入式 避无可避的知识点&#xff0c;但是网络的层次很多&#xff0c;很多时候我们根本理解不了其中的层次和作用&#xff0c;今天跟我们公司的 X 总聊到这个&#xff0c;给我普及了一些知识&#xff0c;我觉得非常有用&#xff0c;分享给大家。最近事…

常用排序算法以及算法性能测试(完整C/C++代码实现)

排序算法性能的比较 注: 由于只是测试算法性能, 所以不会对排序算法做深入讲解, 在随后的时间将会推出排序的详细讲解 问题需求分析 排序算法经过了很长时间的演变&#xff0c;产生了很多种不同的方法。每种算法主要针对不同的数列进行排序&#xff0c;这些排序算法具有各自…

我就随便BB一下

&#xff11;.如果我开始写文章的时候&#xff0c;停顿了几分钟&#xff0c;那结果应该很明显&#xff0c;这一定是一篇比较垃圾的文章&#xff0c;没什么东西值得看&#xff0c;所以我把之前的删除了&#xff0c;重新开头来写&#xff0c;我认为&#xff0c;一个好的开头一定是…